やろーじだい

ブログです

Aizu-SICP 第七回

第七回をやりました。

今回の内容

P59 1.3 高階手続きによる抽象の定式化 ~ P65 1.3.2 lambda を使って手続きを構築する 終わりまで

 今回は質疑を含めて読書会中に言及したものを含めて補足のように書いた。

1.3 高階手続きによる抽象の定式化

私達が普段まとめて「関数」と呼ぶところを、この本では、プログラミング的な意味では「手続き」という単語を、数学的な意味では「関数」という単語を使用しているので今後もこれに倣うようにする (以前これについて言及されている部分があったと記憶しているのだが見当たらなかった)。

1.3.1 引数としての手続き

項を計算するのに identity (恒等) 手続きを使うと、sum によって sum-integers を定義できます。
P62

さらっと出ているが、恒等手続きはある値に任意の手続きを適用できるように抽象化を行った際に、「なにも適用しない」ということをするためによく使われる重要なものなので、初めて触れる概念の場合は覚えておきたい。

P63 練習問題 1.29 ~ P65 1.33 まで

ここは一旦飛ばした

1.3.2 lambda を使って手続きを構築する

lambda という名前でなく、make-procedure (手続き作成) のような当たり前の名前をつけたほうが、 Lisp を勉強する人にはわかりやすく、怖そうな感じを与えないですんだかもしれません。
P67 注釈 53

比較的新しいプログラミング言語の場合は functionfn という単語を使う場合が多いのではないかと思ったので、適当に思いついた言語のラムダ式の定義方法を調べてみた。

C++ (構文) 参考

#include <iostream>
#include <string>
using namespace std;
int main(int argc, char const* argv[])
{
        string x = "I am string";
        [&] { cout << x << endl; } (); //参照
        [=] { cout << x << endl; } (); //コピー
        return 0;
}

Erlang (fun) 参考

> F = fun(X) -> X * X end.
#Fun<erl_eval.6.80247286>
> F(100).
10000

Go (func) 参考

func main() {
    fn := func() {
        fmt.Println("hello")
    }
    fn()
}

Haskell (: バックスラッシュ) 参考

inc = \x -> x + 1

main = do
    print $ inc 5

\(バックスラッシュ)をギリシア文字の λ(ラムダ)に見立てています。

OCaml (fun) 参考

# (fun x -> x * x) 5;;
- : int = 25

Python (lambda) 参考

num = (lambda x,y:x + y)(3,4)
print num    # 7

Rust (構文) 参考

// 引数リスト(i32, i32)をとって戻り値i32を返すクロージャ
let add = |x, y| { x - y };
let sub = |x, y| x + y;
println!("{} {}", add(1,2), sub(1,2));

// 引数なし/戻り値()のクロージャを宣言&呼出し(意味はない)
(||{})();

Scala (構文) 参考

(x: Int) => x * x 

lambda という単語を意識して使っているのは一部 (挙げたものの中では HaskellPython) であり、function (ここでの procedure) という単語を中心にするものや、予約語を使わずに構文で表現してしまうものも増えているようだった。事実 lambda という単語からは無名関数を作りだすものとしてのイメージが湧き辛いため今後もこの流れが多そうだ。

let を使って局所変数を作る

P69 の繰り返しになるが、 let がネストした例として更に以下に示す。

(let ([x 1])
  (let ([x 3]
        [y (+ x 3)])
    (+ x y)))
;=> 7

直感的には 9 になるのではないかと考える人もいるかもしれないが以下のように lambda 式で置き替えることができる。

((lambda [x] 
         ((lambda [x y] (+ x y))
          3 
          (+ x 3)))
 1)
;=> 7

したがって内側の lambda 式の引数に含まれる x と仮引数を表す x は無関係になる。そのため、展開前の例の内側の let にある (+ x 3)(+ 1 3) となるので結果は 7 になる。

P70 練習 1.34

(define (f g) (g 2))
(f f)

これの結果はどうなるかという問題で、安易に無限ループするのだろうと考えてしまった。しかし以下のように展開されるので無限ループには入らずエラーで止まる。

まず f というものは (lmabda (g) (g 2)) であると考えられる。

(f f)

((lambda (g) (g 2)) 2)

ここでラムダ式g2 を受けとるが 2 は関数ではないので *** ERROR: invalid application: (2 2) というエラーで停止する。