08 Jun 2015, 13:06

SICP 第 2 章 データによる抽象の構築を読んだ

SICP 第 2 章を読み終わった?のでなんか書きます.

感想

第 1 章を読み終わってから 3 ヶ月が経っていた…

実は一旦挫折した. 問題が難しくて解けないからだ. しかし、ある時発想を変えて、とにかく写経しながら進めようと思った.

問題は解けないけれども、分からないわけではないので、理解できる範囲で頑張ろうと思った. すると、一旦は折れた心も再び立ち上がることができて、先に進めるようになった.

あと、ところどころ本流に関係なさそうな部分は飛ばしながら読んだ. ズル読み.

あと、Emacs lisp で問題をといていたが、途中で Scheme に変更した. ときどき、動かないプログラムがでてくるので.

要点

データ抽象・抽象の壁

constructor(構成子), selector(選択子) によって、どう使うかに関するプログラムの 抽象の壁 を構築し、抽象レイヤを構築する.

これを、Data Abstruction(データ抽象) という.

データオブジェクトをどう表現するかに関するプログラムの部分を、 データオブジェクトをどう使うかに関するプログラムから隔離する.

データによるレイヤー構造を構築することで, 複雑なシステムをうまく構築することができる.

  • ある場所での変更を局所的なレイヤの変更に封じこめることができる
  • 実装方法(どう表現するか)を自由に入れ替えることができる.

有理数の構成子 make-rat と 選択子 numer, denum を利用することで、 有理数の演算を定義できる.

;; --------------------------------------
(defun cons (x y) ...)
(defun car (x) ...)
(defun cdr (x) ...)
;; --------------------------------------
(defun make-rat (n d) (cons n d))
(defun numer (x) (car x))
(defun denum (x) (cdr x))
;; --------------------------------------
;; このレイヤは car cdr cons は意識しなくていい
;; numer,denum がどう実装されていても気にしない.
(defun add-rat (x y)
  (make-rat (+ (* (numer x) (denom y)
                  (numer y) (denom x)))
            (* (denum x) (denum y))))
;; --------------------------------------

所感

カプセル化との違いが分からない. 状態を持たないということだろうか?

公認インタフェース 

並びを操作するための手続き.

  • map
  • filter
  • accumulate
  • flatmap

公認インタフェースを組み合わせることで、リストを自由に操作できる.

いろいろなブログをみると、ここで Lisp に目覚めるひとがおおいとか?!

所感

unix のパイプラインと似ている. 部品を組み合わせることことよって、データを処理する.

こういう処理を自由自在にかけるようになりたい!

cat file.txt | sort | cut ....

実装例

(defun accumulate (op initial sequence)
  (if (null sequence)
      initial
      (funcall op (car sequence)
               (accumulate op initial (cdr sequence)))))

(defun map (p seq)
  (accumulate (lambda (x y) (cons (funcall p x) y))
              nil
              seq))

(defun filter (predicate sequence)
  (cond ((null sequence) nil)
        ((funcall predicate (car sequence))
         (cons (car sequence)
               (filter predicate (cdr sequence))))
        (t (filter predicate  (cdr sequence)))))

02 Mar 2015, 13:21

SICP 手続きによる抽象の構築を読んだ

はじめに

SICP を今年から読んでいます. 第一章を読むのに 2 ヶ月かかった!

こりゃ, 全部読みきるのに 1 年くらいはかかりそうだ… or2.

気長に楽しみながら頑張る. 切りがいいので, ここまでの要点をまとめてみる.

手続きとは

大事な, そしてややこしい用語がでてくるので, 整理する.

式 (Expression)

計算機の解釈系に渡される前の表現. 解釈系に評価されると, 式はプロセスになる.

以下, wikipedia からの引用も.

言語によって定められた優先順位や結びつきの規定に則って評価 される値, 変数, 演算子, 関数の組み合わせ.

プロセス (Process)

プロセスは計算機のなかに潜む抽象的な存在. プロセスはもう一つの抽象的な存在, データを操作する. プロセスの進行は, 規則のパターン, プログラムにしたがう.

プログラム (Program)

プロセスの進行を指示する, 規則のパターン.

プログラムは二つの要素をもつ.

  • 手続き: データの処理方法 (能動的) ex.) +, -
  • データ: 処理したいもの (受動的) ex.) 1,2,3

手続きによる抽象の構築

この章のおもしろいところは, はじめは基本的な手続きからはじまり, ひとつずつ抽象度をあげていくところにある.

章でいうと, 1.1 と 1.3. 1.2 は再帰手続きについてかかれている.

                     procedures       data

primitive element +, *, <, = 23, 1.738 means of combination () combination
if
cond
means of abstraction defun

言語は以下の仕掛けを有している.

Level0: 基本式 (プリミティブな手続きの構築)

もっとも基本的な手続き.

  • which represent the simplest entities the language is concerned with,
  • 言語が関わるもっとも単純なものを表す.

primitive expressions 基本式:

  • which represent the simplest entities the language is concerned with,
  • 言語が関わるもっとも単純なものを表す.
1, +, -

Level1: 組合せ法 (組み合わせ手続きによる抽象の構築)

複数の手続きを組み合わせて一つにした手続き.

  • by which compound elements are built from simpler ones.
  • より単純なものから合成物をつくる.

Emacs Lisp では 組合せ (combination) は () で表現する.

(* 1 1)

Level2: 抽象化法 (名前つき値による抽象の構築)

オブジェクトを値 (value) とする変数 (variable) を識別するものが名前. 名前をつけることで, 値を識別する.

Emacs Lisp では 名前つけは defun で表現する.

(defun size () 2)
(size)

Level3: 手続き定義 (名前つき手続きによる抽象の構築)

名前付けは, 値だけでなくて手続きにもできる.

  • by which compound elements can be named and manipulated as units.
  • 合成物に名をつけ, 単一のもとして扱う.

名前のつけられた手続き. これをいわゆる関数と呼ぶ.

(defun square (x) (* x x))

手続き定義は, 細部をかくすことができる. いわゆる 手続き抽象 という.

Level4: 高階手続きによる抽象の構築

手続きをあつかう手続きを高階手続きという

  • 手続きを引数にとる
  • 手続きを戻り値として返す
(square (square (square 2)))

Level5: lambda (名前なし手続き による抽象の構築)

高階手続きの引数にいちいち, defun で定義された手続きをわかすのは煩わしい.

名前なしの手続きを扱いたい. プロセスを生み出す特殊形式を lambda という.

Emacs Lisp では lambda で表現する.

名前つき手続きは, 以下の糖衣構文となっている. Lisp インタプリタは実際には以下のように解釈している.

(defun square
    (lambda (x) (* x x))

さいごに

さいごに, 感動したセンテンスをぬきだして, 第一章を締めくくる.

われわれはプログラマとして, プログラムの根底にある抽象をみつけ, より強力な抽象化ができるように努めてなければならない.

高階手続きの重要さは, それにより抽象をプログラム言語の要素して 確かに表せ, 他の計算要素として扱えるようになる点にある.

26 Feb 2015, 14:54

Emacs Lisp で SICP に挑戦するさいの落とし穴

はじめに

SICP を Emacs Lisp でとくという, やや無謀なことに挑戦中.

SICP 自体は Scheme をベースにかかれているので, 例題や回答を elisp に置き換えながら読んでいく.

Emacs Lisp は不自由なもので, 結構ハマリポイントがあった.

第一章がようやく終わったところで, いったんたまったノウハウをはきだしておこう.

よくある置き換え

defun

毎回ある置き換え.

;; scheme
(define (hoge x) x)

;; emacs lisp
(defun hoge (x) x)

cond

cond の書き方について,

  • scheme は 一番最後に else をつける.
  • elips は 一番最後に t をつける.
(cond ((= 1 1) 1)
      (else 2))

(cond ((= 1 1) 1)
      (t 2))

高階関数

関数にの頭に ‘をつけて別の関数の引数にすると, 関数は評価されずに引数に渡すことができる.

評価するときは, (funcall f) のように funcall を呼ぶ.

ローカル変数

elips は scheme のように, 関数の中に関数をかいても 変数や関数のスコープを限定できない.

let, let*, letrec が利用をうまくつかって, ローカルな変数や関数をつかう.

ローカル関数の定義

let + lambda を利用する, 変数に無名関数を bind させることで実現する.

(let ((p (lambda (a) (message a))))
    (funcall p "hoge"))

letrec を利用する方が正式か?

letrec の rec は 再帰のこと. let は再帰関数が定義できないが, letrec はできる.

落とし穴

Dynamic Scope

Emacs Lisp は Dynamic Scope という方式をとり, 名前空間がない. なので, 安直な a とか sum とかいう関数を名づけてしまうと, うっかり他の関数と競合する.

できるだけ, 他とかぶらない関数名をつけるほうが無難.

Emacs24 からは, ファイルの先頭に以下を書くと, setq で宣言した変数は本当の Lexical Scope になる.

-*- lexical-binding: t -*- 

高階関数で 関数がわたせない

無名関数を入力しても, なぜか Symbol value as void となる.

以下を行頭に買いて, M-x eval-buffer で評価することで, 回避できた.

;; -*- lexical-binding: t -*- 

max-lisp-eval-depth 発生

Emacs Lisp は 末尾最適化がされないため, 深い再帰処理をかくと, よくクラッシュする.

これによって, 解くのを諦めた問題多数. これが Elisp の限界か.

その他

自分一人で行き詰まった時は, 先人の知恵を拝借する.

15 Jan 2015, 15:59

Emacs Lisp の例外処理について軽く調べてみた

はじめに

さっき, Java の例外処理について調べて見たので, こんどは Emacs Lisp の例外処理について調べてみた.

文法

error

<div class="outline-text-3" id="text-unnumbered-3">
  <p>
    致命的なエラーが発生した時に利用. 実行中の elisp を強制停止.
  </p>

  <p>
    [sourcecode language=&#8221;emacs-lisp&#8221; title=&#8221;&#8221;]<br /> (error "%s" "Fatal Error Occured!!!")<br /> [/sourcecode]
  </p>
</div>

signal

<div class="outline-text-3" id="text-unnumbered-4">
  <p>
    エラーシンボルとデータを伴って例外をあげる.
  </p>

  <p>
    [sourcecode language=&#8221;emacs-lisp&#8221; title=&#8221;&#8221;]<br /> (signal &#8216;wrong-type-argument &#8216;(0))<br /> [/sourcecode]
  </p>

  <p>
    java における throw new HogeException (&#8220;hoge&#8221;);
  </p>
</div>

condition-case

<div class="outline-text-3" id="text-unnumbered-5">
  <p>
    エラーを補足する.
  </p>

  <p>
    [sourcecode language=&#8221;emacs-lisp&#8221; title=&#8221;&#8221;]<br /> (defun error-test ()<br /> (interactive)<br /> (condition-case error-var<br /> (/ 0 0)<br /> (message "%s" error-var)))<br /> (error-test)<br /> [/sourcecode]
  </p>

  <p>
    java における catch のような役割.
  </p>
</div>

unwind-protect

<div class="outline-text-3" id="text-unnumbered-6">
  <p>
    後処理をする.
  </p>

  <p>
    [sourcecode language=&#8221;emacs-lisp&#8221; title=&#8221;&#8221;]<br /> (defun error-test2 ()<br /> (interactive)<br /> (unwind-protect<br /> (/ 0 0)<br /> (message "%s" "you shall die!!")))<br /> (error-test)<br /> [/sourcecode]
  </p>

  <p>
    java における finally のような役割.
  </p>
</div>

02 Jan 2015, 15:41

2015 年の書き初め! Emacs Lisp をはじめて書いてみた

はじめに

今年の目標は, Emacs Lisp がかけるようになること.

というわけで, 新年早々, Emacs Lisp をはじめて書いてみた.

書き初めみたいな. ちなみに, 初夢はなにもみなかった.

はじめて??

はじめて というと, 正確にはウソ.

今までは他人の書いた Code をコピペしたり, 部分的に書き換えたりしながらつかってきた.

今回は, コピペではなく, 文法を理解しつつ書いたという意味で, はじめて.

文法の勉強

まずは, 文法を知らないとなにもできないので,

以下の本のはじめのほうを読む.

書いたもの

もちろん, 他の人の書いたものをベースにして書いた.

ERC で キーワード が呼ばれたら, Growl で通知

Linux では erc-nick-notify があるのだけれども, Windows 環境で 通知する elisp がなかったので,つくってみた.

つくってみたのだが, テストできないので, テストしていないという…

元ネタはこれ.

Windows 環境での Growl の設定は以下を参考にした.

Growl を利用すると, Alt+x, Alt+Shift+x が利用できなくなるという 致命的な問題がある. なんと, M-x が利用できない!

%USERPROFILE%\Local Settings\Application Data\Growl\2.0.0.0\user.config

で Alt+X とかいてあるところをべつのものに修正すればいい.

つくったもの

というわけで, 作成したのは以下.

初仕事は会社で elisp のデバッグだ!!

休憩時間をタイマーではかる

休憩時間に休憩中というメッセージを画面に表示して, 休憩時間を計測する elisp.

こんな感じ.

以下を参考にした.

つくったもの

02 Jan 2015, 10:41

Emacs での eval について調べてみた

はじめに

Elisp を評価 (eval) する方法について調べてみました.

代表的なコマンド

以下の 3 つが代表的.

  • eval-buffer バッファを評価
  • eval-region リージョンを評価
  • eval-last-sexp C-x C-e 最後のかっこを評価

参考:

インタラクティブに評価

こんな感じや, こんな感じに,

片方のウィンドウで評価したら別のウィンドウで動作させたいので, 以下の関数を書いてみた.

(defun my-inf-elisp ()
  (interactive)
  (eval-buffer)
  (if (one-window-p)
    (split-window))
  (other-window 1)
  (eshell)
)
(define-key emacs-lisp-mode-map (kbd "C-c S") 'my-inf-elisp)