プロセスはプログラムに基づき動き、データを操作する。
なぜLispが使われるのか
"受動的な"データと"能動的な"プロセスという伝統的な区別を
曖昧にする能力を使った強力なプログラム設計のテクニックが存在する
**個人的な解釈として手続きをデータとして扱う事ができる事をLambda等と解釈しています **
- 基本式: 言語に関わる最も単純な実体
val x: Int = 0
- 組み合わせ方法:複合要素をより単純なものから構築する方法
{
val x: Int = 0;
y = x * 10;
}
- 抽象化方法:複合要素に名前をつけ単体として扱う方法
def hoge() = {
val x: Int = 0;
y = x * 10;
}
プログラミングは手続きとデータを扱う。
- 手続き-> 規則
- データ-> 物
- 基本式 -> 数値
- 手続き式 -> +, -, /, *
- 複合式 -> 手続き式と基本式を組み合わせた式
式をインタプリタに入力すると評価されそれに基づき、結果が表示される。
組み合わせ (combination): 複合式に渡す式の組み合わせの事。
リストの要素は**演算子 (operator) ** と ** 被演算子 (operand)からなる。
被演算子に渡す基本式に事を引数 (argument)**と呼ぶ
(+ (* 3 (+ (* 2 4) (+ 3 5))) (+ (- 10 7) 6))
を
(+ (* 3
(+ (* 2 4)
(+ 3 5)))
(+ (- 10 7)
6))
と整形した者をプリティプリントと呼ぶ。
*式を端末から読み、式を評価し、結果を表示するサイクルを
**インタプリタのREPL(read-eval-print loop) **と呼ぶ
プログラミング言語は**変数 (variable) **を名前をつけて扱う事ができる。
値 (value) がそのオブジェクトである 変数 (variable) を名前によって特定 する
Lispでは以下のように"size","pi"という名前に2, 3.14159を関連付けます。
-> 2 という値を名前(size)によって参照できる。
(define size 2)
(define pi 3.14159)
size
2
名前とオブジェクトのペアを記録しておくメモリを環境と呼ぶ ハッシュテーブル的な物。 一般的な環境を グローバル環境 (global environment) と呼ぶ
インタプリタが式を強化する時、2つの事を行う
- 組み合わせの部分式を評価する
- 部分式の左端 (演算子) の値となっている手続きを、引数 (被 演算子)、つまり部分式の残り値に適用する
組み合わせ式を評価する時、構成している式から評価していかないといけない。
中には呼び出している式自体をよび出す場合もある。
つまり評価規則は再帰的 (recursive) な物となる。
組み合わせ式は木で表現する事ができる
“値を上に向かって伝える” という形の評価規則は、木の集積 (tree accumulation) として知られている。
// ToDo Add Image
木を深くまで潜って行くとある地点で組み合わせ式ではない数値や組み込み演算子、名前などにたどり着く。その場合は
- 数字の値は、それが示す値である
- 組み込み演算子の値は機械語の列で、それに対応する操作を実行する
- その他の名前の値は、現在の環境でその名前に関連づけられたオブジェ
クトである
と規定する。これを 一般的評価規則 と呼ぶ
ここで2つ目の規則は3番目の規則の特殊な例でしかない。なぜなら
機械語の命令自体を"値"として関連付ける事ができるからである。
例として
(define x 5)
(+ x 1)
だと、"x"に関連づけられた値(5)と1に対し(+)に関連づけられた機械語命令(add r1...)を適用する。 全ての動作は環境によって決められる。
*特例形式
(define x 3)
の場合
記号 x の値と 3 という 二つの引数に define を適用するのではなく
x(名前)に3という値を関連付ける事が目的なので上の一般的評価規則には当たらない。
このような規則を 特殊形式 (special form) と呼ぶ。
上で学んだLispでできる事
- 数値は基本データで、算術演算は基本手続きである。
- 組み合わせをネストすることで、演算を組み合わせることができる。
- 定義は名前と値を関連づけ、抽象化のためにある程度役に立つ。
**手続きの定義 (procedure de nition) **とは?
まず2乗の定義は
ある数(x)を2乗するとは、ある数(x)にある数(x)自身をかける
である。これをLispで書き直すと
(define (square x) (* x x))
となる。内容としては**複合手続き (compound procedure) *( x x)にsquareと言う
名前をつけているだけである。上の式を一般化すると
(define (⟨名前⟩ ⟨仮引数⟩) ⟨本体⟩)
となる。
基本的に評価して行く時
複合手続きを引数に適用するには、手続きの本体に出てくる仮引数を対応する引数で
置き換えて、それを評価する。 を繰り返している。 置換する事でどのように動いているかをわかりやすくする事ができる。
評価を行う手順としては二種類ある
- 引数を評価し、その結果をもとに上位の式を評価していく
- 基本演算のみになるまで置き換えを繰り返し、基本演算のみになった時点で評価して行く
上の方を 適用順序評価 (applicative-order evaluation)
下の方を 正規順序評価 (normal-order evaluation)
と呼ぶ。上の方は複雑になってしまったり同じ式を複数回評価していまい性能が落ちる。
Lispは 場合分け(case analysis) を実装する事ができる。
(define (abs x)
(cond ((> x 0) x)
((= x 0) 0)
**cond (“conditional”**を使う 上の例はxの絶対値を返す。 condを一般化すると
(cond(⟨p1⟩ ⟨e1⟩)
(⟨p2⟩ ⟨e2⟩)
...
(⟨pn⟩ ⟨en⟩))
となる。かっこの事を**節 (clause)**と呼び節は(⟨p⟩ ⟨e⟩)で構成されている。
述語 (predicate)⟨e⟩ と 結果式 (consequent expression) ⟨e⟩ 。
condは述語が真になるまで進み、真になった場合結果式を返す。
(if ⟨predicate⟩ ⟨consequent⟩ ⟨alternative⟩)
(and ⟨e1⟩ . . . ⟨en⟩)
一つでも偽になる述語があると偽を返す。全て真の場合、最後の式の値を返す。
(or ⟨e1⟩ . . . ⟨en⟩)
一つでも真がある場合、その式の値を返す。
(not ⟨e⟩)
無論
- and, orは特殊形式である。なぜかというと全ての部分式が評価されるわけではないからである。 andの使い方の例
(and (> x 5) (< x 10))
x が 5 < x < 10 という値域にあるの意