top page > computer > haskell > web_lecture > for_programmer > nsc_lambda.html
更新日:
文責: 重城良国

Nano Scheme: 関数定義と呼び出し

(工事中 30%)

はじめに

(lambda (x) (* x x))のように無名関数の構築子をつくる。(define f (lambda (x) (* x x)))とすれば関数が定義できる。同じことを構文糖(define (f x) (* x x))でできるようにする。

ラムダ

Environment.hs

ここではこのような「関数のようなもの」をラムダと呼ぶ。schemeでは環境を閉じこめるという意味でクロージャと呼ぶ。今回は環境の閉じこめはしない。クロージャとは呼べない。ラムダを表現する値をValue型に追加する。

data Value
.
.
.
| Lmbd [Symbol] Value

表示する。

...
showValue (Lmbd _ _) = "#<lambda>"

構文lambda

Primitive.hs

ラムダを作成する構文lambdaを定義する。lambda関数を定義して

lambda :: [Value] -> Env -> Maybe (Value, Env)
lambda [vs, v] e = (\ps -> (Lmbd ps v, e)) `mapply` symbols vs
lambda _ _ = Nothing

symbols :: Value -> Maybe [Symbol]
symbols (List vs) = ss vs
where
ss (Symbol s : vs') = (s :) `mapply` ss vs'
ss [] = Just []
ss _ = Nothing
symbols _ = Nothing

lambda変数を定義する。

env0 = fromList [
.
.
.
("lambda", Sntx lambda),
.
.

ラムダの適用

Eval.hs

ラムダを適用するには実引数を仮引数に代入して本体部分を評価する。ラムダの実行後には新しく作られた変数は捨てる。

apply (Lmbd ps bd) vs e = (`mbind` evaluate vs e) $ \(as, e') ->
(\(r, _) -> (r, e'))
`mapply` (eval bd . foldr (uncurry set) e $ zip ps as)

読みやすさのために(\(as, e') -> ...) `mbind` evaluate vs eとする代わりに(`mbind` evaluate vs e) $ \(as, e') -> ...とした。

[定石] 2引数関数で第1引数が長くなるとき(`f` a2) $ ...のようにすると読みやすくなる。とくに第1引数が関数のときは(`f` a2) $ \aa -> ...のようにできる。

試してみる

lambdaで作ったラムダをすぐに使う例だ。

echo 'nsc ((lambda (x) (* x x)) 8)' | runghc -Wall nsc.hs
64

defineで定義してから使う。

echo 'nsc (define f (lambda (x) (* x x))) (f 9)' | runghc -Wall nsc.hs
f
81

関数定義を行う構文糖

関数の作成と変数への定義とを同時に行う構文糖を作成する。関数defineに動作を追加する。構文木を組み立ててevalしてしまえば簡単だ。

define [sm@(Sym s), v] e = ...
define [List (f : ps), v] e =
eval (List [Sym "define", f, List [Sym "lambda", List ps, v]]) e

試してみる

コマンドラインで

echo 'nsc (define (f x) (* x x)) (f 9)' | runghc -Wall nsc.hs
f
81

Hubotで

Hubotを再起動して

nsc (define (f x) (* x x)) (f 9)

としてみよう。

まとめ

関数定義ができるようになった。

「Nano Scheme: 変数定義」へもどる 「Nano Scheme: 構文if」へ

正当なCSSです! HTML5 Powered with CSS3 / styling, and Semantics