2008年06月10日

SICPの面白かったところをメモしておきます。

SICPの
Exercise 2.6. In case representing pairs as procedures wasn't
mind-boggling enough,
ていうところが面白かった。
わーって思ったから。

2008/08/01なんでわーって思ったかわすれてたから書いておきます。
「2.1.3 What Is Meant by Data?」から。

(define (cons x y)
(define (dispatch m)
(cond ((= m 0) x)
((= m 1) y)
(else (error "Argument not 0 or 1 -- CONS" m))))
dispatch)

(define (car z) (z 0))

(define (cdr z) (z 1))

consの戻り値はクロージャ!で、それをcarとかcdrに渡すというわけです。わーっ!
posted by ほえ at 18:14| Comment(0) | TrackBack(0) | Scheme | このブログの読者になる | 更新情報をチェックする

2008年02月15日

レキシカルスコープ(lexical scope、静的スコープ、構文スコープ)・ダイナミックスコープ(dynamic scope、動的スコープ)が間違っていた件

昔の記事を見てみたら、ずいぶんと間違っていたので書きなおしてみます。

動的スコープ、静的スコープ両方の変数が使用できるperlが一番わかりやすいようです。
localで動的スコープ、myで静的スコープの変数が作成できます。myはperl5から使えるそうです。

#!/usr/bin/perl

sub sub_parent {
local $dynamic_var = 10;
my $lexical_var = 20;
&sub_child;
}

sub sub_child {
print "dynamic_var = $dynamic_var\n";
print "lexical_var = $lexical_var\n";
}

&sub_parent;

-> dynamic_var = 10
lexical_var =


動的スコープはそのコードが実行される際に親のスコープを参照できます。
上の例で、sub_child内で$dynamic_varは、呼出元sub_parentの値を参照できるので、
dynamic_var = 10

と表示されています。

静的スコープはそのコードの構文のみでスコープが決定します。
上の例で、sub_child内で$lexical_varは、呼出元の値を参照しないので
lexical_var = 

と何も表示されていません。


今度はこんなにしてみました。

#!/usr/bin/perl

sub sub_parent {
local $dynamic_var = 10;
my $lexical_var = 20;

sub sub_child {
print "dynamic_var = $dynamic_var\n";
print "lexical_var = $lexical_var\n";
}

sub sub_other {
local $dynamic_var = 100;
my $lexical_var = 200;
&sub_child;
}

&sub_other;
}

&sub_parent;

-> dynamic_var = 100
lexical_var = 20


sub_parent内にsub_childとsub_otherというサブルーチンを作りました。
sub_perentを呼ぶと、sub_otherを呼び出します。
sub_otherでは、$dynamic_varを100に、$lexical_varを200にしてからsub_childを呼び出しています。
動的スコープの変数$dynamic_varは
dynamic_var = 100

と10->100に変更されて表示されていますが、
静的スコープの変数$lexical_varは
lexical_var = 20

と20->200に変更されていません。

これはコードを先頭から構文解析する時に(ホント?)、$lexical_varの値が決定しているからです。


perlでもサブルーチンを返すサブルーチンが書けたので、こんなにしてみました。

#!/usr/bin/perl

sub hoe {
local $a = @_[0];
my $b = @_[1];
return sub {
print "a = $a\n";
print "b = $b\n";
}
}

sub hoehoe {
$pfunc = @_[0];

local $a = 1000;
my $b = 2000;
&$pfunc;
}

$f1 = &hoe(10, 20);
$f2 = &hoe(100, 200);

&hoehoe($f2);
-> a = 1000
b = 200

&hoehoe($f1);
-> a = 1000
b = 20

$aは動的スコープの変数なので、hoehoe内で呼ばれる時に、hoehoe内で与えられた値1000を表示しています。
$bは静的スコープの変数なので、hoeでサブルーチンが作成された時の値@_[1]つまりhoeの2番目の引数の値20または200を表示しています。

また、$f1、$f2の順でサブルーチンが作成されていますが、それぞれ作成された時の値20、200を覚えています。これはクロージャの話ですね。
静的スコープでリテラルでない値を参照する時の話になると、クロージャとは切り離せないのでしょうか?リテラルでの話の場合だとstatic変数って事じゃないの?って思ってしまうのです。


JavaScriptの事。JavaScriptは静的スコープです。

function hoe(a) {
return function() {
alert(a);
}
}
var f1 = hoe(10);
var f2 = hoe(20);
f2(); -> 20
f1(); -> 10

こんなです。


ここからはいろいろ間違っていた話。

(define a 100)
(define bar (display a))
(define foo (let ((a 10)) bar))

(foo) -> 100

コレはエラーになっちゃいますね。
少なくともコウですね。

(define a 100)
(define (bar) (display a))
(define (foo) (let ((a 10)) bar))

((foo)) -> 100

letの事がわからなくなっちゃったので、保留…。

JavaScriptの間違っていた(?)話。
確かに、VBとかCとかは動的スコープ。C#はきっと静的スコープも使えるようになっているのではないかと。最近の事はしらない。
で、JavaScriptは動的スコープではない。静的スコープです。
で、コード

a = 100;
function bar() {
alert(a);
}
function foo() {
a = 10;
bar();
}

foo(); -> 10

は確かに10になるけど、これは単にaが全部グローバル変数を参照しているだけです。
このfoo();の後にalert(a);と書くと10です。
JavaScriptの変数のスコープは関数なので、

var a = 100;
function bar() {
alert(a);
}
function foo() {
var a = 10;
bar();
}

foo(); -> 100

こうでないとおかしいです。100になる。なので、静的スコープとは関係ない話に。関係ないわけではないけど、グローバル変数を参照するという話です。グローバル変数ってスコープ関係なく参照できますもんね。普通。何が普通かって。


phpは動的スコープだけど、変わってる。

$a = 100;
function hoe()
{
echo 'a = '.$a;
}

hoe(); -> a =

とaの値は出力されません。

$a = 100;
function hoe()
{
global $a;
echo 'a = '.$a;
}

hoe(); -> a = 100;

phpのスコープも関数ですが、その外側の変数を参照しようとすると、globalキーワードを使用しなければなりません。
何回これにハマった事か…。
globalを使うコードを書かなきゃいけないアプリの書き方がいけないと思うんですけど…。

なんだかえらく長くなりました。
最初からperlで試してみていれば、レキシカルスコープって?って悩まなくて済んでいたような気がします。おしまい。
posted by ほえ at 17:40| Comment(0) | TrackBack(0) | Scheme | このブログの読者になる | 更新情報をチェックする

EmacsでGaucheの便利な件

Emacsで、Gaucheを編集?するときに便利な機能がありました。のでメモ。

C-cS (M-x scheme-other-window) goshを別ウィンドウで走らせる。
※M-x run-schemeだとそのウィンドウでgoshが実行されるので、何か編集中のときは別ウィンドウで出てくれた方が便利ですね。
C-c C-r (M-x scheme-send-region) 選択中のリージョンを評価(実行)する
C-c M-r (M-x scheme-send-region-and-go) C-c C-rとの違いがわからない…。
C-c C-l (M-x scheme-load-file) ファイルを読み込んで評価する

まだまだ。
posted by ほえ at 10:17| Comment(0) | TrackBack(0) | Scheme | このブログの読者になる | 更新情報をチェックする

2006年07月07日

リテラル その2

(quote a) -> a
'a -> a
ですが
''a -> 'a
(quote 'a) -> 'a
'(quote a) -> 'a
(quote (quote a)) -> 'a
''a -> 'a
です。
(quote (quote a)) -> (quote a)
ではないのですね。Gaucheでの話ですが。R5RSでは
'(quote a) -> (quote a)
''a -> (quote a)
とあります。
posted by ほえ at 10:47| Comment(2) | TrackBack(0) | Scheme | このブログの読者になる | 更新情報をチェックする

2006年07月04日

真理値

(boolean? obj)
objが真理値の場合#tを返します。
(boolean? #t) -> #t
です。また、
(boolean? #f) -> #t
です。#fでも#tを返します。
(boolean? 1) -> #f
(boolean? "abc") -> #f
(boolean? (list 1 2 3 4)) -> #f
(boolean? '()) -> #f
です。

(not obj)
objが偽であれば#tを返します。それ以外は#fを返します。
(not #f) -> #t
(not #t) -> #f
(not 1) -> #f
(not "abc") -> #f
(not (list 1 2 3 4)) -> #f
(not '()) -> #f
です。
値が論理値として評価される場合は、#f以外は全て#tに評価されるということでもあります。


posted by ほえ at 00:09| Comment(0) | TrackBack(0) | Scheme | このブログの読者になる | 更新情報をチェックする

2006年07月02日

外部表現

整数28の外部表現は文字列"28"。
整数8と13からなるリストの外部表現は、文字列"(8 13)"。

整数28は"#e28.000"でも"#x1c"でも。
整数8と13からなるリストは"( 08 13 )"でも"(8 . (13 . ()))"でも。

"(+ 2 6)"は、整数8に評価される式ですが、整数8の外部表現ではありません。
要素が三つのリストの外部表現です。
posted by ほえ at 14:49| Comment(0) | TrackBack(0) | Scheme | このブログの読者になる | 更新情報をチェックする

2006年07月01日

lambda式、仮引数

また、後戻りです。
((lambda x x) 3 4 5 6) -> (3 4 5 6)
これを見たとき、????でした。前に書いてます。
(lambda 仮引数 <ボディ>)
仮引数に括弧がありません。
実引数はリストに変換されます。引数の数は不定個です。
なのです。

こうして行きつ戻りつして進んでいくのです。チータァるんるん
posted by ほえ at 23:37| Comment(0) | TrackBack(0) | Scheme | このブログの読者になる | 更新情報をチェックする

副作用(side effect)

「値を返すというのが関数の定義であるが、関数が値を返すまでに行なう処理の過程で、値を返す以外の動作を関数の外に及ぼす場合にそれを副作用という。」だそうです。
posted by ほえ at 14:47| Comment(0) | TrackBack(0) | Scheme | このブログの読者になる | 更新情報をチェックする

begin(順次処理手続き、順次処理式、順次実行、直列化)

(begin <式1> <式2> ...)
式は左から右へ順番に評価され、最後の式の値が返されます。
(begin 
(define x 1)
(define y (+ x 2))
(define z (+ x y))
) -> z
z -> 4
x -> 1
y -> 3

この式は入出力などの副作用を順番に処理するために使用されるそうです。
また、一つの式を書く場所に複数の式を使いたいとき、begin式を使用すると中の式が順番に評価されて、最後の式の値が返されるという使い方もできるそうです。

posted by ほえ at 14:29| Comment(1) | TrackBack(0) | Scheme | このブログの読者になる | 更新情報をチェックする

2006年06月21日

named-let書き換え

なんでもλにある1から100まで加算するコード
(let loop ((num 1)
(sum 0))
(if (> num 100)
sum
(loop (+ num 1) (+ sum num)))
)

をletrecを使って書き換えます。
((letrec ((loop (lambda (num sum)
(if (> num 100)
sum
(loop (+ num 1) (+ sum num))
)
)
))
loop)
1 0)
ですね。
posted by ほえ at 20:56| Comment(1) | TrackBack(0) | Scheme | このブログの読者になる | 更新情報をチェックする

2006年06月18日

名前つきlet(named-let)

ちょっと話は飛んでしまうのですが、名前つきlet(named-let)です。
(let <変数> <バインディング> <ボディ>)
なのです。
なんでもλのサンプル1から100までを加算するコード
(let loop ((num 1)
(sum 0))
(if (> num 100)
sum
(loop (+ num 1) (+ sum num)))
)
を見て、血迷ってletをlet*にしてみたり、letrecにしてみたりしました。

...、
(let* <変数> <バインディング> <ボディ>)

(letrec <変数> <バインディング> <ボディ>)
は無いんです。

(let <変数> <バインディング> <ボディ>)
という構文なのです。
前に書いた、(let <バインディング> <ボディ>)のバインディング構成手続きとは別物なのです。

let*やletrecにすると、単純にsyntax-errorになるのです。

繰り返しに使うらしいです。名前つきletについては、また、書きます。
posted by ほえ at 21:43| Comment(0) | TrackBack(0) | Scheme | このブログの読者になる | 更新情報をチェックする

2006年06月17日

引数?

前回の続きで
(define (bar (set! x 5) x))
としたら、
(bar 1 2) -> #undef
の時の引数1, 2はどうなっちゃったんでしょう?
ボディなしでも何らかの手続きが行われるという事なんですね。

(define (whatis a b c) a b c)
だと
(whatis 1 2 3) -> 3
になるんですね。

なんとなく、消化不良。

追記です...
ここだけプロンプト付で
gosh> 1
1
gosh> (1 2 3 4)
**** ERROR: invalid application: (1 2 3 4)
Stack Trace: ...

gosh> 1 2 3 4 -> 1
1
gosh> 2
gosh> 3
gosh> 4
gosh>
なんですね。
posted by ほえ at 23:40| Comment(0) | TrackBack(0) | Scheme | このブログの読者になる | 更新情報をチェックする

式と手続き(defineで定義3)

(define a 1) -> a
a -> 1
(a) -> error

(define foo (+ 1 2)) -> foo
foo -> 3
(foo) -> error

(define (foo) (+ 1 2)) -> foo
foo -> #<closure foo>
(foo) -> 3
当たり前なんだけど、なんだか混乱してしまいました。
なんでかというと

(define x 100)
x -> 100
(x) -> error

(define bar (set! x 100))
bar -> 100
(bar) -> error

なんてやっていて、何を血迷ったか
(define (bar (set! x 5) x))

なんてやってしまったものだから、

bar -> #<closure foo>
(bar) -> error ;引数が2個必要なのでエラー
(bar 1 2) -> #<undef> ;こんな事になってしまいました。
ちなみに
x -> 100

です。

これは何の事はない、ボディのない手続きを定義してしまったのです。(set! x 5)が引数1で、xが引数2という事ですね。
本当は
(define (bar) (set! x 5))


(bar) -> 5
x -> 5
としてみたかったのです。

結果。
(define <変数> <式>) は<変数>を定義する式
(define (<変数> <仮引数群>) <ボディ>) は名前が<変数>の手続きを定義する式
(define (<変数> . <仮引数>) <ボディ>) も同じく、名前が<変数>の手続きを定義する式
という事があまりよくわかっていなかった。という事です。

posted by ほえ at 23:34| Comment(0) | TrackBack(0) | Scheme | このブログの読者になる | 更新情報をチェックする

レキシカルスコープ(lexical scope、静的スコープ、構文スコープ)・ダイナミックスコープ(dynamic scope、動的スコープ)

スコープとはバインディングの有効範囲です。
Schemeは静的スコープです。または構文スコープ、レキシカルスコープともいいます。

()の外側から内側のバインディングを参照することができません。
()の内側から外側のバインディングを参照することはできます。

#いろいろ間違ってる…。訂正を書きます。2008/02/15

(define a 100)

(define bar
(display a))

(define foo
(let ((a 10)) bar))

(foo)->100
関数手続きfooから呼び出されている関数手続きbarではaはグローバル変数のaを参照しています。
関数手続きfooで作成された変数aのスコープはfooの記述範囲のみ。

※動的スコープ、またはダイナミックスコープ
Emacs Lispはダイナミックスコープ
(foo)=>10

ダイナミックスコープでは関数手続きbarは、呼び出し元関数手続きfooでのaを参照している。

VBとかCとかC#とかもろもろはダイナミックスコープ
たとえばJavaScripでは
a = 100;
function bar() {
document.write(a);
}
function foo() {
a = 10;
bar();
}
foo();
=>10

perlのmyはレキシカルスコープ、localはダイナミックスコープだそうです。

posted by ほえ at 19:47| Comment(0) | TrackBack(1) | Scheme | このブログの読者になる | 更新情報をチェックする

let, let*, letrec (変数束縛、バインディング構成手続き、バインディング作成手続き)

変数をその値に束縛して、ボディを評価します。
つまりlet達のボディで利用する、ローカル変数が使えるって事ですか?
手続きに束縛されているローカル変数もですね。<- 何が言いたいかというと「ローカル関数(手続き)も使える」と思ったのですが、変数が手続きに束縛されていればもちろんその手続きも利用できるのですね。

<バインディング>は((<変数1> <初期値1>) (<変数2> <初期値2>)...)

(let <バインディング> <ボディ>)
変数のバインディングが行なわれる以前に、初期値が計算されます。
つまり最初に初期値を評価してしまうので、
((変数1 初期値1) (変数2 初期値2))
の時、初期値2を評価する時には、変数1のバインディングは見られないわけですね。
(let ((x 1) (y 2)) (+ x y)) -> 3
(define x 10) -> 10
(
let (
(x 1) ;<- ローカルな x に 1
(y x) ;<- ローカルな y にグローバルな x = 10
)
(+ x y)
) -> 11 ;


(let* <バインディング> <ボディ>)
バインディングと評価が順次的に行なわれます。
つまり書いてある順番に評価されるので、
((変数1 初期値1) (変数2 初期値2))
の時、変数1のバインディング、初期値1の評価が行われた後になるので、初期値2を評価する時には、変数1のバインディングを見ることができるのですね。
(let ((x 1) (y 2)) (+ x y)) -> 3
(define x 10) -> 10
(let (
(x 1) ;<- ローカルな x に 1
(y x) ;<- ローカルな y にローカルな x = 1
)
(+ x y)
) -> 2 ;



(letrec <バインディング> <ボディ>)
初期値が計算されている間にもバインディングが有効であり、したがって相互に再帰的な定義が可能です。<- 意味がわかりません。
最初にバインディングが行われ、次に初期値が計算されるという事です。
つまり、変数1、変数2...は未定義の値を保存する新しい記憶域にバインドされ、その環境で初期値が評価される。そして、その環境でボディが評価される。という事です。

環境という言葉を使わざるを得ませんでした。なんとなくわかるんですけど...ネ。

さて、letrecでは、各変数のバインディング(値はまだ計算されていません)がletrec本体(ボディ)からだけでなく、初期化部でも見えているわけです。
let*でも同じように他の変数が初期化部でも見えていますが、あくまで書かれた順に評価されていくわけなので、後に書かれている変数は見ることができません。

(let* (
(x y) ;<- y が定義されていないとエラーになってしまう。
(y 1)
)
(+ x y)
)

(letrec (
(x y)
(y 1)
)
(+ x y)
)
-> 2

とletrecではエラーになりません。要するに、こうであると再帰に利用できると。

さて、それでは、それぞれどういう場合に使うのかという事を考えて見ます。
まず、一番わかりやすいのはletrecです。再帰に使用する場合はこれしかありません。
つぎにlet*は、変数の初期化時に、ローカルの変数を使用したい時に使用します。
最後にletは、再帰も、ローカル変数の初期化にローカル変数も使用しない時に使用します。
letの外側の変数と、letで使用する変数が重複しても問題ないように、普段はlet*を使用するべきなんでしょうか。それならいっそのことletrecを使用したほうが??

何かいい例がないでしょうか???
posted by ほえ at 15:23| Comment(0) | TrackBack(0) | Scheme | このブログの読者になる | 更新情報をチェックする

2006年06月16日

lambda式

(lambda <仮引数> <ボディ>)
名前のない手続きに評価される式です。
lambdaに実引数を渡すには、ラムダ式と実引数を括弧でくくる。
<仮引数>は以下の通り。

(lambda (仮引数1 仮引数2 ... 仮引数n) <ボディ>)
決まった数の引数をとります。
((lambda (x) x) 5) -> 5
((lambda (x) (+ x x)) 5) -> 10
((lambda (x y) (+ x y)) 1 2) -> 3


(lambda 仮引数 <ボディ>)
仮引数に括弧がありません。
実引数はリストに変換されます。引数の数は不定個です。
((lambda x x) 1) -> (1)
((lambda x (apply + x)) 1) -> 1
((lambda x (apply + x)) 1 2 3 4 5) -> 15


(lambda (仮引数1 仮引数2 ... 仮引数n-1 . 仮引数n) <ボディ>)
1〜n-1の仮引数に対応する実引数は、対応する変数に束縛されます(この言い方は正しいのかな?)。残りの実引数がリストに変換されます。引数の数は不定個です。
((lambda (x y . z) z) 1 2 3) -> (3)
((lambda (x y . z) z) 1 2 3 4 5 6) -> (3 4 5 6)
((lambda (x y . z) (apply + x y z)) 1 2 3 4 5 6) -> 21


define式を使って、名前を付けられます。
(define add-all (lambda (x y . z) (apply + x y z)))
(add-all 1 2 3 4 5 6) -> 21
これは
(define (add-all x y . z) (apply + x y z))
と書くことができます。

posted by ほえ at 16:43| Comment(0) | TrackBack(0) | Scheme | このブログの読者になる | 更新情報をチェックする

set!

(set! <変数> <式>)
<変数>がバインドされている記憶域に、<式>を評価した値が保管される。
つまり<変数>は既に存在している必要があります。ということ?
posted by ほえ at 01:22| Comment(0) | TrackBack(0) | Scheme | このブログの読者になる | 更新情報をチェックする

2006年06月15日

仮引数と実引数

(define (add x y) (+ x y))
の時、(add x y)のxとyが仮引数。
(add 1 2) -> 3
の時、1と2が実引数。

いまさら...。
posted by ほえ at 18:31| Comment(0) | TrackBack(0) | Scheme | このブログの読者になる | 更新情報をチェックする

バインド(束縛)

入門Schemeから
defineの動作は一定の記憶域を未使用の記憶域からSchemeに割り当てた上でそこに式を評価した結果(オブジェクト)を入れ、その記憶域に変数(名)を連結する。したがって変数に値が入るのではなく、値は変数が連結されている記憶域に入る。
なるほど。連結はバインドの事です。
posted by ほえ at 17:47| Comment(0) | TrackBack(0) | Scheme | このブログの読者になる | 更新情報をチェックする

defineで定義2

(define <変数> <式>)
(define x 1)
x -> 1
(define x (+ 1 2))
x -> 3


(define (<変数> <仮引数群>) <ボディ>)
(define (add-one x) (+ x 1))
(add-one 1)
-> 2
(define (add x y) (+ x y))
(add 1 2)
-> 3
これは引数の数が固定されるという事です。
(add 1 2 3)
-> *** ERROR: wrong number of arguments for # (required 2, got 3


(define (<変数> . <仮引数>) <ボディ>)

(define (add-all . x) (apply + x))
(add-all 10) -> 10
(add-all 1 2 3 4 5) -> 15
これは仮引数が、リストとしてボディに渡されるという事です。
なので、ボディでの処理にリスト処理が必要です。
applyの部分は+をリストxの要素に適用するという組み込み手続きです。
(define (add-all . x) (+ x 1))
(add-all 10) -> *** ERROR: operation + is not defined between 1 and (10)
posted by ほえ at 02:01| Comment(0) | TrackBack(0) | Scheme | このブログの読者になる | 更新情報をチェックする

広告


この広告は60日以上更新がないブログに表示がされております。

以下のいずれかの方法で非表示にすることが可能です。

・記事の投稿、編集をおこなう
・マイブログの【設定】 > 【広告設定】 より、「60日間更新が無い場合」 の 「広告を表示しない」にチェックを入れて保存する。


×

この広告は1年以上新しい記事の投稿がないブログに表示されております。