Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
7347 lines (6712 sloc) 220 KB
;;呼ばれるとき自動的にsaveoff
;;;;setwindow 20, 20, 24, 31, 14, 14, 0, 2, 20, 1, 1, "img/msgbox.bmp", 10, 10
;;;;setwindow 95, 480, 21, 3, 29, 29, 0, 4, 20, 0, 1, "img/msgbox2.bmp", 80, 470
*text_cw
erasetextwindow 0
getcursorpos %tmp1, %tmp2
amsp SP_CLICK, %tmp1, %tmp2
amsp SP_CLICK_MINI, %tmp1, %tmp2
if %adv_talkmode == 1 amsp SP_CLICK, 675, 546 ;talkmodeなら、右下に星を表示
vsp SP_CLICK, 1
if %adv_talkmode == 100 vsp SP_CLICK_MINI, 1 : vsp SP_CLICK, 0 ;speakmodeなら小さい星を表示
print 1
;;;
btndef clear
if %adv_talkmode == 0 goto *text_cw_l1 ;not talk mode
if %adv_talkmode == 100 goto *text_cw_l1 ;speak mode
if %adv_noroi_off == 1 goto *text_cw_l1
exbtn_d "P816C817" ;816=SP_USAGI, 817=SP_USAGI_R
exbtn SP_USAGI, 1001, "C816P817"
*text_cw_l1
;;;
if %save_flag == 1 saveon
*text_cw_loop
getpage ;PageUp, PageDownを拾う
textbtnwait %ret
if %ret == 0 goto *text_cw_next ;left click
if %ret == -1 gosub *text_cw_rclick ;right click
if %ret == -12 gosub *text_cw_pageup
if %ret == -2 gosub *text_cw_pageup
if %ret == 101 gosub *text_cw_pageup
if %ret == 1001 gosub *noroi_loop
goto *text_cw_loop
*text_cw_next
vsp SP_USAGI, 0
vsp SP_USAGI_R, 0
vsp SP_CLICK, 0
vsp SP_CLICK_MINI, 0
print 1
texec ;改ページの場合テキストを消す
erasetextwindow 1
saveoff
return
*text_cw_pageup
vsp SP_CLICK, 0
vsp SP_CLICK_MINI, 0
print 1
systemcall lookback
vsp SP_CLICK, 1
if %adv_talkmode == 100 vsp SP_CLICK, 0 : vsp SP_CLICK_MINI, 1
print 1
return
*text_cw_rclick
if %adv_rmode == 0 return
vsp SP_CLICK, 0
vsp SP_CLICK_MINI, 0
print 1
gosub *rclick_menu
;; systemcall rmenu
vsp SP_CLICK, 1
if %adv_talkmode == 100 vsp SP_CLICK, 0 : vsp SP_CLICK_MINI, 1
print 1
return
*rclick_menu
lsp SP_RC_BACK, ":c;img/black.bmp", 0, 0, 150
lsp SP_RC_1, ":s/30,30,1;#DDDDDD#FFFFFF文字を消す", 300, 140
lsp SP_RC_2, ":s/30,30,1;#DDDDDD#FFFFFF回想", 300, 170
lsp SP_RC_3, ":s/30,30,1;#DDDDDD#FFFFFFセーブする", 300, 200
lsp SP_RC_4, ":s/30,30,1;#DDDDDD#FFFFFFロードする", 300, 230
lsp SP_RC_5, ":s/30,30,1;#DDDDDD#FFFFFFインタプリタを起動", 300, 260
print 1
btndef clear
spbtn SP_RC_1, 1
spbtn SP_RC_2, 2
spbtn SP_RC_3, 3
spbtn SP_RC_4, 4
spbtn SP_RC_5, 5
*rclick_menu_loop
btnwait2 %ret
if %ret == 0 goto *rclick_menu_loop
csp SP_RC_1
csp SP_RC_2
csp SP_RC_3
csp SP_RC_4
csp SP_RC_5
csp SP_RC_BACK
if %ret == 1 systemcall windowerase
if %ret == 2 systemcall lookback
if %ret == 3 systemcall save
if %ret == 4 systemcall load
if %ret == 5 gosub *free_eval
btndef clear
print 1
return
*free_eval
mov %gc_silent, 1
mov %adv_rmode, 0 ;右クリック無効
lsp SP_RC_BACK, ":c;img/black.bmp", 0, 0, 240
print 1
skipoff
mov %save_flag, 0 ;セーブ不能
gosub *create_new_env ;自由にいじれるように新しい環境を作る
mov %arg0, %ret
mov %toplevel_env, %arg0
gosub *push ;GC対策
mov %arg1, %global_env
gosub *nconc
*free_eval_loop
mov %gc_run, 0 ;GC起動回数を初期化
mov %current_proc, %nil
mov %current_env, %toplevel_env
mov %adv_error, 0
mov $sarg0, ""
lsp SP_RC_1, ":s/14,14,1;#FFFFFFTabキーでゲームに戻ります", 40, 100
print 1
textfield $sarg0, 30, 120, 770, 140, 10, 20, 0
getret %ret
if %ret == 1 goto *free_eval_end ;Tab
if $sarg0 == "" goto *free_eval_loop
lsp SP_RC_1, ":s/14,14,1;#FFFFFF計算中……", 40, 100
print 1
repaint
gosub *check_lr_parenthesis
if %ret != 0 gosub *free_eval_parenthesis_error : goto *free_eval_loop
gosub *input_to_lobject
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *eval_form
mov %arg0, %ret
gosub *get_tag : if %ret == TAG_ERROR mov %adv_error, 1
gosub *lobject_to_string
;;改行表示処理
mov $stmp1, ":s/14,14,1;#FFFFFF"
mov $stmp2, ":s/14,14,1;#FFFFFF"
mov $stmp3, ":s/14,14,1;#FFFFFF"
if %adv_error == 1 goto *free_eval_l0
mov $sret, "結果は『" + $sret + "』です"
*free_eval_l0
len %tmp, $sret
if %tmp <= 80 mov $stmp, ":s/14,14,1;#FFFFFF"+$sret : goto *free_eval_l1
;;2行目突入
mid $stmp, $sret, 0, 80
mid $sret, $sret, 80, %tmp-80
mov $stmp, ":s/14,14,1;#FFFFFF"+$stmp
len %tmp, $sret
if %tmp <= 80 mov $stmp1, ":s/14,14,1;#FFFFFF"+$sret : goto *free_eval_l1
;;3行目突入
mid $stmp1, $sret, 0, 80
mid $sret, $sret, 80, %tmp-80
mov $stmp1, ":s/14,14,1;#FFFFFF"+$stmp1
len %tmp, $sret
if %tmp <= 80 mov $stmp2, ":s/14,14,1;#FFFFFF"+$sret : goto *free_eval_l1
;;4行目突入
mid $stmp2, $sret, 0, 80
mid $sret, $sret, 80, 80
mov $stmp2, ":s/14,14,1;#FFFFFF"+$stmp2
mov $stmp3, ":s/14,14,1;#FFFFFF"+$sret
*free_eval_l1
lsp SP_RC_1, $stmp, 40, 180
lsp SP_RC_2, $stmp1, 40, 194
lsp SP_RC_3, $stmp2, 40, 208
lsp SP_RC_4, $stmp3, 40, 222
print 1
click
csp SP_RC_2
csp SP_RC_3
csp SP_RC_4
goto *free_eval_loop
*free_eval_end
mov %gc_silent, 0
mov %adv_rmode, 1 ;右クリック有効
gosub *pop
saveon
mov %save_flag, 1 ;セーブ可能
csp SP_RC_1
csp SP_RC_BACK
print 1
return
*free_eval_parenthesis_error
lsp SP_RC_1, ":s/14,14,1;#FFFFFF括弧の開く数と、閉じる数が一致していません", 40, 100
print 1
click
return
;;呼ばれるとき自動的にsaveoff
*customsel
btndef clear
getcselnum %tmp
getnextline %tmp1, %tmp3
getcursorpos %tmp1, %tmp2
sub %tmp3, %tmp2
for %i=0 to %tmp-1
cselbtn %i, %i+1, %tmp1, %tmp2
add %tmp2, %tmp3 ;文字の高さ
next
*customsel_loop
getpage ;PageUp, PageDownを拾う
selectbtnwait %ret
if %ret == -12 gosub *text_cw_pageup2 : goto *customsel
if %ret == -2 gosub *text_cw_pageup2 : goto *customsel
if %ret <= 0 goto *customsel_loop
if %ret > %tmp goto *customsel_loop
saveon
cselgoto %ret-1
*text_cw_pageup2
systemcall lookback
return
*noroi_loop
rnd %adv_noroi, 530
add %adv_noroi, 85
amsp SP_NOROI_HALF, %adv_noroi, 338
amsp SP_NOROI, %adv_noroi, 338
amsp SP_NOROI_D1, %adv_noroi, 338
amsp SP_NOROI_D2, %adv_noroi, 338
amsp SP_NOROI_HD1, %adv_noroi, 338
amsp SP_NOROI_HD2, %adv_noroi, 338
lsph SP_TMP, ":s/30,30,1;#FFFFFF撃破数", 550, 40
itoa2 $sarg0, %adv_noroi_kill
if %adv_noroi_kill < 10 mov $sarg0, ":s/50,50,0;#FFFFFF" + $sarg0
if %adv_noroi_kill >= 10 mov $sarg0, ":s/30,50,0;#FFFFFF" + $sarg0
lsph SP_TMP1, $sarg0, 650, 20
vsp SP_NOROI_HALF, 1
vsp SP_TMP, 1
vsp SP_TMP1, 1
mov %adv_noroi_frame, 0
*noroi_loop_l1
resettimer
print 1
waittimer 33
;;
trap off
inc %adv_noroi_frame
if %adv_noroi_frame == 10 vsp SP_NOROI_HALF, 0 : vsp SP_NOROI, 1
if %adv_noroi_frame == 20 vsp SP_NOROI_HALF, 1 : vsp SP_NOROI, 0
trap *noroi_loop_judge
;;
if %adv_noroi_frame < 30 goto *noroi_loop_l1
vsp SP_NOROI_HALF, 0
csp SP_TMP
csp SP_TMP1
trap off
delay 200
print 1
return
*noroi_loop_judge
getmousepos %adv_noroi_mx, %adv_noroi_my
if %adv_noroi_frame <10 goto *noroi_loop_judge_h
if %adv_noroi_frame >=20 goto *noroi_loop_judge_h
if %adv_noroi_mx > %adv_noroi+20 && %adv_noroi_mx < %adv_noroi+85 && %adv_noroi_my > 388 && %adv_noroi_my < 470 goto *noroi_loop_hit
goto *noroi_loop_l1
*noroi_loop_judge_h
if %adv_noroi_mx > %adv_noroi+20 && %adv_noroi_mx < %adv_noroi+85 && %adv_noroi_my > 403 && %adv_noroi_my < 470 goto *noroi_loop_hit_h
goto *noroi_loop_l1
*noroi_loop_hit
inc %adv_noroi_kill
mov %adv_noroi_frame, 0
vsp SP_NOROI, 0
vsp SP_NOROI_D1, 1
csp SP_TMP1
itoa2 $sarg0, %adv_noroi_kill
if %adv_noroi_kill < 10 mov $sarg0, ":s/50,50,1;#FFFFFF" + $sarg0
if %adv_noroi_kill >= 10 mov $sarg0, ":s/30,50,0;#FFFFFF" + $sarg0
lsp SP_TMP1, $sarg0, 650, 20
*noroi_loop_hit_loop
resettimer
if %adv_noroi_frame == 6 vsp SP_NOROI_D1, 0 : vsp SP_NOROI_D2, 1
inc %adv_noroi_frame
print 1
waittimer 33
if %adv_noroi_frame < 12 goto *noroi_loop_hit_loop
vsp SP_NOROI_D2, 0
csp SP_TMP
csp SP_TMP1
print 1
return
*noroi_loop_hit_h
inc %adv_noroi_kill
mov %adv_noroi_frame, 0
vsp SP_NOROI_HALF, 0
vsp SP_NOROI_HD1, 1
itoa2 $sarg0, %adv_noroi_kill
if %adv_noroi_kill < 10 mov $sarg0, ":s/50,50,1;#FFFFFF" + $sarg0
if %adv_noroi_kill >= 10 mov $sarg0, ":s/30,50,0;#FFFFFF" + $sarg0
lsp SP_TMP1, $sarg0, 650, 20
*noroi_loop_hit_loop_h
resettimer
if %adv_noroi_frame == 6 vsp SP_NOROI_HD1, 0 : vsp SP_NOROI_HD2, 1
inc %adv_noroi_frame
print 1
waittimer 33
if %adv_noroi_frame < 12 goto *noroi_loop_hit_loop_h
vsp SP_NOROI_HD2, 0
csp SP_TMP
csp SP_TMP1
print 1
return
*confirm_abort
アリサ「やけに時間がかかるわね……無限ループの可能性があるわ。計算を中止する?」
csel "中止", *confirm_abort_abort, "続行", *confirm_abort_resume
*confirm_abort_resume
mov %eval_count, 0
return
*confirm_abort_abort
return
*parenthesis_error
textclear
r_show ari_die
アリサ「括弧の開く数と、閉じる数が一致していないわよ」\
r_show ari_n
return
*trap_init
trap *trap_init_l1
*trap_init_l1
trap off
return
*img_init
lsph SP_CLICK, ":l/4,<200,200,200,200>,0;img/click.bmp", 0, 0
lsph SP_CLICK_MINI, ":l/4,<200,200,200,200>,0;img/click_m.bmp", 0, 0;;;
lsph SP_USAGI, ":c;img/usagi.bmp", 0, 445
lsph SP_USAGI_R, ":c;img/usagi_r.bmp", 0, 445
lsph SP_NOROI_HALF, ":l;img/noroi_half.bmp", 0, 338
lsph SP_NOROI, ":l;img/noroi.bmp", 0, 338
lsph SP_NOROI_D1, ":l;img/noroi_d1.bmp", 0, 338
lsph SP_NOROI_D2, ":l;img/noroi_d2.bmp", 0, 338
lsph SP_NOROI_HD1, ":l;img/noroi_hd1.bmp", 0, 338
lsph SP_NOROI_HD2, ":l;img/noroi_hd2.bmp", 0, 338
lsph SP_DUMMY, ":s#000000な", 1000, 1000
return
*show_wait_picture
csp SP_TMP
rnd %adv_tmp, 2
if %adv_tmp == 0 lsp SP_TMP, ":c;img/moji.bmp", 0, 0
if %adv_tmp == 1 lsp SP_TMP, ":c;img/nanyano.bmp", 0, 0
getspsize SP_TMP, %adv_tmp1, %adv_tmp2
div %adv_tmp1, 2
mov %adv_tmp1, 400 - %adv_tmp1
mov %adv_tmp2, 590 - %adv_tmp2
amsp SP_TMP, %adv_tmp1, %adv_tmp2
print 1
return
*ex_init
mov %adv_rmode, 0 ;右クリック無効
bgm ms_ex
csp SP_R : csp SP_L
bg black, E_NORMAL
r_load ari_n
bg "img/blackboard.bmp", E_FAST
skipoff
mov %adv_tmp, 0
mov %save_flag, 0 ;セーブ不能
*ex_init_env
gosub *create_new_env ;自由にいじれるように新しい環境を作る
mov %arg0, %ret
mov %toplevel_env, %arg0
gosub *push ;GC対策
mov %arg1, %global_env
gosub *nconc
return
*ex_rep
mov %gc_run, 0 ;GC起動回数を初期化
mov %current_proc, %nil
mov %current_env, %toplevel_env
mov %adv_error, 0
if %in_ex_test != 0 goto *ex_rep_get_answer
mov $sarg0, ""
textclear
gosub $problem_label
textfield $sarg0, 30, 120, 770, 140, 10, 20, 0
getret %ret
if %ret == 1 goto $tab_label ;Tab
if $sarg0 == "" goto $ret_label
textclear
!s0アリサ「計算中……」!sd
repaint
gosub *check_lr_parenthesis
if %ret != 0 gosub *parenthesis_error : goto $ret_label
goto *ex_rep_exec
*ex_rep_get_answer
gosub *ex_test_set_answer
*ex_rep_exec
gosub *input_to_lobject
mov %arg0, %ret
gosub *push
mov %arg1, %toplevel_env
gosub *eval_form
mov %arg0, %ret
gosub *get_tag : if %ret == TAG_ERROR mov %adv_error, 1
gosub *push
gosub *lobject_to_string
if %in_ex_test != 0 goto $suc_label
textclear
if %adv_error == 1 goto *ex_rep_error
アリサ「結果は『$sret』ね」\
goto $suc_label
*ex_rep_error
アリサ「プログラムに誤りがあるわ」
#33FF33$sret#FFFFFF\
goto $suc_label
;;; ex_check_pass($label)
*ex_check_pass
if %in_ex_test == 0 goto $sarg0
goto *ex_test_pass
;;; ex_check_pass_end($label)
*ex_check_pass_end
if %in_ex_test == 0 goto $sarg0
goto *ex_test_pass_end
;;; ex_check_fail($label)
*ex_check_fail
if %in_ex_test == 0 goto $sarg0
goto *ex_test_fail
;;; ex_test_set_answer()
*ex_test_set_answer
if $suc_label == "*ex02_check" mov $sarg0, "(define x 4)"
if $suc_label == "*ex02_2_check" mov $sarg0, "(* (+ x 3) 4)"
if $suc_label == "*ex03_check" mov $sarg0, "'hayate"
if $suc_label == "*ex03_2_check" mov $sarg0, "(define fate 'nanoha)"
if $suc_label == "*ex03_3_check" mov $sarg0, "fate"
if $suc_label == "*ex04_check" mov $sarg0, "(define x '(a b c))"
if $suc_label == "*ex04_2_check" mov $sarg0, "(car x)"
if $suc_label == "*ex04_3_check" mov $sarg0, "(cdr x)"
if $suc_label == "*ex05_check" mov $sarg0, "(define (f x) (car (cdr x)))"
if $suc_label == "*ex05_2_check" mov $sarg0, "(f '(a b c d))"
if $suc_label == "*ex06_check" mov $sarg0, "(define (s n) (if (= n 0) 0 (+ n (s (- n 1)))))"
if $suc_label == "*ex06_2_check" mov $sarg0, "(s 10)"
if $suc_label == "*ex07_check" mov $sarg0, "(define (len x) (if (null? x) 0 (+ 1 (len (cdr x)))))"
if $suc_label == "*ex07_2_check" mov $sarg0, "(len '(a b c))"
if $suc_label == "*ex08_check" mov $sarg0, "(define (f x) (lambda (y) (+ x y)))"
if $suc_label == "*ex08_2_check" mov $sarg0, "((f 6) 66)"
if $suc_label == "*ex09_check" mov $sarg0, "(define (si n acc) (if (= n 0) acc (si (- n 1) (+ acc n))))"
if $suc_label == "*ex09_2_check" mov $sarg0, "(s 10)"
if $suc_label == "*ex11_check" mov $sarg0, "(define (map1 f x) (if (null? x) x (cons (f (car x)) (map1 f (cdr x)))))"
if $suc_label == "*ex11_2_check" mov $sarg0, "(map1 (lambda (n) (+ n 1)) '(1 2))"
if $suc_label == "*ex12_check" mov $sarg0, "(define (muli x y acc) (if (eq? y (zero)) acc (muli x (dec y) (add x acc))))"
if $suc_label == "*ex12_2_check" mov $sarg0, "(mul (inc (inc (zero))) (inc (inc (inc (zero)))))"
return
;;; ex_test_pass_log($test_name)
*ex_test_pass_log
gosub *print_string
!s0【#00FF00pass#FFFFFF】 $sret!sd
return
;;; ex_test_pass($label)
*ex_test_pass
gosub *spush
mov $sarg0, $problem_label
gosub *ex_test_pass_log
gosub *spop
goto $sret
;;; ex_test_pass_end($ignored_label)
*ex_test_pass_end
mov $sarg0, $problem_label
gosub *ex_test_pass_log
goto *ex_test_next_problem
;;; ex_test_fail($ignored_label)
*ex_test_fail
mov $sarg0, $problem_label
gosub *print_string
!s0【#FF0000fail#FFFFFF】 $sret!sd
*ex_test_next_problem
mov %sp, %in_ex_test ; restore %sp
gosub *ex_init_env
if $suc_label == "*ex02_check" goto *ex03
if $suc_label == "*ex02_2_check" goto *ex03
if $suc_label == "*ex03_check" goto *ex04
if $suc_label == "*ex03_2_check" goto *ex04
if $suc_label == "*ex03_3_check" goto *ex04
if $suc_label == "*ex04_check" goto *ex05
if $suc_label == "*ex04_2_check" goto *ex05
if $suc_label == "*ex04_3_check" goto *ex05
if $suc_label == "*ex05_check" goto *ex06
if $suc_label == "*ex05_2_check" goto *ex06
if $suc_label == "*ex06_check" goto *ex07
if $suc_label == "*ex06_2_check" goto *ex07
if $suc_label == "*ex07_check" goto *ex08
if $suc_label == "*ex07_2_check" goto *ex08
if $suc_label == "*ex08_check" goto *ex09_prev
if $suc_label == "*ex08_2_check" goto *ex09_prev
if $suc_label == "*ex09_check" goto *ex11
if $suc_label == "*ex09_2_check" goto *ex11
if $suc_label == "*ex11_check" goto *ex12_prev
if $suc_label == "*ex11_2_check" goto *ex12_prev
if $suc_label == "*ex12_check" goto *ex_test_done
if $suc_label == "*ex12_2_check" goto *ex_test_done
*ex_test_done
done.\
mov %sp, %in_ex_test ; restore %sp
mov %in_ex_test, 0
return
*ex_test_start
mov %in_ex_test, %sp ; save %sp
gosub *ex_init_env
goto *ex02
;;;;;;;;;;
;;;すべてはここから始まる……
;;;;;;;;;;
*main_game_start
mov %in_ex_test, 0
mov %adv_rmode, 1 ;;正直かなり苦しい……
bgmfadeout 100
gosub *get_vol
!sd
gosub *img_init
saveoff
;;;;;ロゴ開始
lsph SP_LOGO0, ":l;img/magic1.bmp", 0, 0
lsph SP_LOGO1, ":l;img/magic2.bmp", 0, 0
lsph SP_LOGO_LAMBDA, ":l;img/lambda.bmp", 0, 0
lsph SP_LOGO_KUMI, ":l;img/kumi.bmp", 0, 0
lsph SP_LOGO_SAKURA, ":l;img/sakura.bmp", 0, 0
lsph SP_LOGO_KARASU, ":l;img/karasu.bmp", 0, 0
mov %adv_tmp, 0 ;角度*100
mov %adv_tmp1, 50 ;角速度
gosub *trap_init
trap *logo_loop_end
*logo_loop
resettimer
mov %adv_tmp2, %adv_tmp
div %adv_tmp2, 100
drawfill 0, 0, 0
mov %adv_tmp3, %adv_tmp-25500
if %adv_tmp3 < 0 mov %adv_tmp3, 0
drawsp2 SP_LOGO0, 0, 255-%adv_tmp3/200, 400, 300, 125, 125, %adv_tmp2
drawsp2 SP_LOGO1, 0, 255-%adv_tmp3/200, 400, 300, 125, 125, -%adv_tmp2
drawsp2 SP_LOGO_LAMBDA, 0, %adv_tmp3/200, 400, 300, 125, 125, 0
draw
gettimer %adv_tmp3
if %adv_tmp3 > 33 goto *logo_loop_l1
wait 33-%adv_tmp3
goto *logo_loop_l2
*logo_loop_l1
wait 0
*logo_loop_l2
add %adv_tmp1, 2
add %adv_tmp, %adv_tmp1
if %adv_tmp < 76500 goto *logo_loop
mov %adv_tmp, 400
*logo_loop2
resettimer
drawfill 0, 0, 0
drawsp2 SP_LOGO_LAMBDA, 0, 255, %adv_tmp, 300, 125, 125, 0
draw
sub %adv_tmp, 15
gettimer %adv_tmp3
if %adv_tmp3 > 33 goto *logo_loop2_l1
wait 33-%adv_tmp3
goto *logo_loop2_l2
*logo_loop2_l1
wait 0
*logo_loop2_l2
if %adv_tmp > 100 goto *logo_loop2
mov %adv_tmp, 0
*logo_loop3
resettimer
drawfill 0, 0, 0
drawsp2 SP_LOGO_LAMBDA, 0, 255, 100, 300, 125, 125, 0
drawsp2 SP_LOGO_KUMI, 0, %adv_tmp, 208, 300, 125, 125, 0
drawsp2 SP_LOGO_KARASU, 0, %adv_tmp, 550, 300, 125, 125, 0
draw
add %adv_tmp, 6
gettimer %adv_tmp3
if %adv_tmp3 > 33 goto *logo_loop3_l1
wait 33-%adv_tmp3
goto *logo_loop3_l2
*logo_loop3_l1
wait 0
*logo_loop3_l2
if %adv_tmp < 255 goto *logo_loop3
mov %adv_tmp, -40
*logo_loop4
mov %adv_tmp2, %adv_tmp
mul %adv_tmp2, 125
div %adv_tmp2, 100
resettimer
drawfill 0, 0, 0
drawsp2 SP_LOGO_LAMBDA, 0, 255, 100, 300, 125, 125, 0
drawsp2 SP_LOGO_KUMI, 0, 255, 208, 300, 125, 125, 0
drawsp2 SP_LOGO_SAKURA, 0, 255, 319, %adv_tmp2, 125, 125, -120-%adv_tmp
drawsp2 SP_LOGO_KARASU, 0, 255, 550, 300, 125, 125, 0
draw
add %adv_tmp, 4
gettimer %adv_tmp3
if %adv_tmp3 > 33 goto *logo_loop4_l1
wait 33-%adv_tmp3
goto *logo_loop4_l2
*logo_loop4_l1
wait 0
*logo_loop4_l2
if %adv_tmp < 240 goto *logo_loop4
*logo_loop_end
trap off
drawfill 0, 0, 0
drawsp2 SP_LOGO_LAMBDA, 0, 255, 100, 300, 125, 125, 0
drawsp2 SP_LOGO_KUMI, 0, 255, 208, 300, 125, 125, 0
drawsp2 SP_LOGO_SAKURA, 0, 255, 319, 300, 125, 125, 0
drawsp2 SP_LOGO_KARASU, 0, 255, 550, 300, 125, 125, 0
draw
delay 1500
csp SP_LOGO0
csp SP_LOGO1
csp SP_LOGO_LAMBDA
csp SP_LOGO_KUMI
csp SP_LOGO_SAKURA
csp SP_LOGO_KARASU
setwindow 8,16,29,20,26,26,0,2,20,1,1,#FFFFFF,0,0,799,599
gosub *show_wait_picture
!s0
初期化中・・・
このゲームは起動に時間がかかります。
入力に応答しませんので気長にお待ちください。
Please have a coffee break.
!sd
gosub *mem_init
gosub *set_additional_func
mov %gc_silent, 1
gosub *gc
mov %gc_silent, 0
mov %save_flag, 1 ;セーブ可能
textclear
csp SP_TMP
;;;;;タイトルの処理
*title
textoff
mov %adv_miss, 0
mov %adv_noroi_kill, 0
;;共通記録のロード
fileexist %adv_tmp,"arisa.szk"
if %adv_tmp == 0 filecreate "arisa.szk"
csvopen "arisa.szk", "rc"
csvread %adv_clear
csvclose
;;83+40~~120
lsph SP_TITLE, ":l;img/title.bmp", 40, 30
lsph SP_TITLE_SEN, ":l;img/sen.bmp", 10, 266 ;215
lsph SP_TITLE_HOSHI, ":l;img/hoshi.bmp", 175, -30
lsph SP_BACKGROUND, ":c;img/yama.bmp", 0, 0
mov %adv_tmp, 300
gosub *trap_init
trap *title_menu_pre
*title_loop
resettimer
drawsp2 SP_BACKGROUND, 0, 255, 400, 600, 100, 100, 0
drawsp2 SP_TITLE_HOSHI, 0, 255, 365, 160, %adv_tmp, %adv_tmp, 0
drawsp2 SP_TITLE, 0, 255, 422, 167, %adv_tmp, %adv_tmp, 0
draw
gettimer %adv_tmp3
if %adv_tmp3 > 33 goto *title_loop_l1
wait 33 - %adv_tmp3
goto *title_loop_l2
*title_loop_l1
wait 0
*title_loop_l2
sub %adv_tmp, 5
if %adv_tmp > 100 goto *title_loop
drawsp2 SP_BACKGROUND, 0, 255, 400, 600, 100, 100, 0
drawsp2 SP_TITLE_HOSHI, 0, 255, 364, 159, 100, 100, 0
drawsp2 SP_TITLE, 0, 255, 422, 166, 100, 100, 0
draw
vsp SP_TITLE, 1
vsp SP_TITLE_SEN, 1
vsp SP_TITLE_HOSHI, 1
vsp SP_BACKGROUND, 1
print E_RWIPE_SLOW
mov %adv_tmp, 0
*title_loop2
resettimer
amsp SP_BACKGROUND, 0, %adv_tmp, 255
print 1
gettimer %adv_tmp3
if %adv_tmp3 > 33 goto *title_loop2_l1
wait 33 - %adv_tmp3
goto *title_loop2_l2
*title_loop2_l1
wait 0
*title_loop2_l2
sub %adv_tmp, 10
if %adv_tmp > -480 goto *title_loop2
goto *title_menu
*title_menu_pre
*title_menu
trap off
amsp SP_BACKGROUND, 0, -480, 255
vsp SP_TITLE, 1
vsp SP_TITLE_SEN, 1
vsp SP_TITLE_HOSHI, 1
vsp SP_BACKGROUND, 1
print 1
;setwindow2 #FFFFFF
;; setwindow 8,16,20,23,26,26,0,2,20,1,1,#FFFFFF,0,0,639,479
setwindow 8,16,29,20,26,26,0,2,20,1,1,#FFFFFF,0,0,799,599
*title_menu_l1
bgm ms_title
texton
locate 11, 12
if %adv_clear >= 12 csel "最初から", *title_story, "続きから", *title_load, "補足説明", *title_condicil, "用語集", *title_glossary, "フリーモード", *free_mode, "おまけ", *title_omake, "音量調整", *title_set_vol, "終了", *end
csel "最初から", *title_story, "続きから", *title_load, "補足説明", *title_condicil, "用語集", *title_glossary, "フリーモード", *free_mode, "音量調整", *title_set_vol, "終了", *end
*title_set_vol
gosub *set_vol
goto *title_menu_l1
*title_load
systemcall load
goto *title_menu_l1
*title_story
locate 11, 12
if %adv_clear == 0 csel "第1話", *story01_pre, "戻る", *title_menu_l1
if %adv_clear == 1 csel "第1話", *story01_pre, "第2話", *story02_pre, "戻る", *title_menu_l1
if %adv_clear == 2 csel "第1話", *story01_pre, "第2話", *story02_pre, "第3話", *story03_pre, "戻る", *title_menu_l1
if %adv_clear == 3 csel "第1話", *story01_pre, "第2話", *story02_pre, "第3話", *story03_pre, "第4話", *story04_pre, "戻る", *title_menu_l1
if %adv_clear == 4 csel "第1話", *story01_pre, "第2話", *story02_pre, "第3話", *story03_pre, "第4話", *story04_pre, "第5話", *story05_pre, "戻る", *title_menu_l1
if %adv_clear == 5 csel "第1話", *story01_pre, "第2話", *story02_pre, "第3話", *story03_pre, "第4話", *story04_pre, "第5話", *story05_pre, "第6話", *story06_pre, "戻る", *title_menu_l1
if %adv_clear >= 6 csel "第1話", *story01_pre, "第2話", *story02_pre, "第3話", *story03_pre, "第4話", *story04_pre, "第5話", *story05_pre, "第6話", *story06_pre, "次へ", *title_story2, "戻る", *title_menu_l1
;;7話以降は [1話, …, 6話, 次へ] [7話, …, 12話, 戻る] の二つに分割
*title_story2
locate 11, 12
if %adv_clear == 6 csel "第7話", *story07_pre, "前へ", *title_story, "戻る", *title_menu_l1
if %adv_clear == 7 csel "第7話", *story07_pre, "第8話", *story08_pre, "前へ", *title_story, "戻る", *title_menu_l1
if %adv_clear == 8 csel "第7話", *story07_pre, "第8話", *story08_pre, "第9話", *story09_pre, "前へ", *title_story, "戻る", *title_menu_l1
if %adv_clear == 9 csel "第7話", *story07_pre, "第8話", *story08_pre, "第9話", *story09_pre, "第10話", *story10_pre, "前へ", *title_story, "戻る", *title_menu_l1
if %adv_clear == 10 csel "第7話", *story07_pre, "第8話", *story08_pre, "第9話", *story09_pre, "第10話", *story10_pre, "第11話", *story11_pre, "前へ", *title_story, "戻る", *title_menu_l1
if %adv_clear == 11 csel "第7話", *story07_pre, "第8話", *story08_pre, "第9話", *story09_pre, "第10話", *story10_pre, "第11話", *story11_pre, "第12話", *story12_pre, "前へ", *title_story, "戻る", *title_menu_l1
if %adv_clear >= 12 csel "第7話", *story07_pre, "第8話", *story08_pre, "第9話", *story09_pre, "第10話", *story10_pre, "第11話", *story11_pre, "第12話", *story12_pre, "前へ", *title_story, "戻る", *title_menu_l1
*title_omake
locate 9, 15
csel "ラムダ山の合戦", *yakyu, "八神家のストーカーズ!?", *komiyan_start, "エンディングについて", *hint_start, "  戻る", *title_menu_l1
*komiyan_start
gosub *destroy_title
goto *komiyan
*hint_start
gosub *destroy_title
gosub *hint
*title_condicil
gosub *destroy_title
bgm ms_setsume
gosub *condicil
stop
goto *title
*title_glossary
gosub *destroy_title
bgm ms_setsume
gosub *glossary
stop
goto *title
*story01_pre
gosub *destroy_title
goto *story01
*story02_pre
gosub *destroy_title
goto *story02
*story03_pre
gosub *destroy_title
goto *story03
*story04_pre
gosub *destroy_title
goto *story04
*story05_pre
gosub *destroy_title
goto *story05
*story06_pre
gosub *destroy_title
goto *story06
*story07_pre
gosub *destroy_title
goto *story07
*story08_pre
gosub *destroy_title
goto *story08
*story09_pre
gosub *destroy_title
goto *story09
*story10_pre
gosub *destroy_title
goto *story10
*story11_pre
gosub *destroy_title
goto *story11
*story12_pre
gosub *destroy_title
goto *story12
*destroy_title
csp SP_BACKGROUND
csp SP_TITLE
csp SP_TITLE_HOSHI
csp SP_TITLE_SEN
return
*end
end
*r_in
getparam $sarg0
csp SP_R
print E_FAST
lsph SP_R, $sarg0, 0, 0
getspsize SP_R, %tmp1, %tmp2
mov %tmp2, 600-%tmp2
mov %tmp1, 790-%tmp1
mov %tmp, 800
vsp SP_R, 1
trap *r_in_l1
*r_in_loop
resettimer
amsp SP_R, %tmp, %tmp2
print 1
sub %tmp, 30
if %tmp < %tmp1 goto *r_in_l1
gettimer %tmp3
if %tmp3 > 33 goto *r_in_loop_l1
wait 33-%tmp3
goto *r_in_loop_l2
*r_in_loop_l1
wait 0
*r_in_loop_l2
goto *r_in_loop
*r_in_l1
trap off
amsp SP_R, %tmp1, %tmp2
print 1
return
*r_out
getspsize SP_R, %tmp1, %tmp2
mov %tmp2, 600-%tmp2
mov %tmp1, 790-%tmp1
vsp SP_R, 1
trap *r_out_l1
*r_out_loop
resettimer
amsp SP_R, %tmp1, %tmp2
print 1
add %tmp1, 30
if %tmp1 > 800 goto *r_out_l1
gettimer %tmp3
if %tmp3 > 33 goto *r_out_loop_l1
wait 33-%tmp3
goto *r_out_loop_l2
*r_out_loop_l1
wait 0
*r_out_loop_l2
goto *r_out_loop
*r_out_l1
trap off
csp SP_R
print 1
return
*l_rout
getspsize SP_L, %tmp1, %tmp2
mov %tmp2, 600-%tmp2
mov %tmp1, 30
vsp SP_L, 1
trap *l_rout_l1
*l_rout_loop
resettimer
amsp SP_L, %tmp1, %tmp2
print 1
add %tmp1, 30
if %tmp1 > 800 goto *l_rout_l1
gettimer %tmp3
if %tmp3 > 33 goto *l_rout_loop_l1
wait 33-%tmp3
goto *l_rout_loop_l2
*l_rout_loop_l1
wait 0
*l_rout_loop_l2
goto *l_rout_loop
*l_rout_l1
trap off
csp SP_L
print 1
return
*l_in
getparam $sarg0
lsph SP_L, $sarg0, 0, 0
getspsize SP_L, %tmp1, %tmp2
mov %tmp2, 600-%tmp2
mov %tmp, 0-%tmp1
vsp SP_L, 1
trap *l_in_l1
*l_in_loop
resettimer
amsp SP_L, %tmp, %tmp2
print 1
add %tmp, 30
if %tmp > 30 goto *l_in_l1
gettimer %tmp3
if %tmp3 > 33 goto *l_in_loop_l1
wait 33-%tmp3
goto *l_in_loop_l2
*l_in_loop_l1
wait 0
*l_in_loop_l2
goto *l_in_loop
*l_in_l1
trap off
amsp SP_L, 30, %tmp2
print 1
return
*l_out
getspsize SP_L, %tmp1, %tmp2
mov %tmp2, 600-%tmp2
mov %tmp, 0-%tmp1
mov %tmp1, 30
vsp SP_R, 1
trap *l_out_l1
*l_out_loop
resettimer
amsp SP_L, %tmp1, %tmp2
print 1
sub %tmp1, 30
if %tmp1 < %tmp goto *l_out_l1
gettimer %tmp3
if %tmp3 > 33 goto *l_out_loop_l1
wait 33-%tmp3
goto *l_out_loop_l2
*l_out_loop_l1
wait 0
*l_out_loop_l2
goto *l_out_loop
*l_out_l1
trap off
csp SP_L
print 1
return
*talk_mode
mov %adv_talkmode, 1
erasetextwindow 1
setwindow 95, 480, 21, 3, 29, 29, 0, 4, 20, 0, 1, "img/msgbox2.bmp", 80, 470
return
*speak_mode
mov %adv_talkmode, 100
erasetextwindow 0
setwindow 20, 20, 24, 31, 14, 14, 0, 2, 20, 1, 1, "img/msgbox.bmp", 10, 10
return
*r_show
getparam $sarg0
r_load $sarg0
print E_FAST
return
*l_show
getparam $sarg0
l_load $sarg0
print E_FAST
return
*r_load
getparam $sarg0
csp SP_R
lsph SP_R, $sarg0, 0, 0
getspsize SP_R, %tmp1, %tmp2
mov %tmp2, 600-%tmp2
mov %tmp1, 790-%tmp1
amsp SP_R, %tmp1, %tmp2
vsp SP_R, 1
return
*l_load
getparam $sarg0
csp SP_L
lsph SP_L, $sarg0, 0, 0
getspsize SP_L, %tmp1, %tmp2
mov %tmp2, 600-%tmp2
amsp SP_L, 30, %tmp2
vsp SP_L, 1
return
*c_show
getparam $sarg0
c_load $sarg0
print E_FAST
return
*c_load
getparam $sarg0
csp SP_C
lsph SP_C, $sarg0, 0, 0
getspsize SP_C, %tmp1, %tmp2
mov %tmp2, 600-%tmp2
mov %tmp1, %tmp1/2
mov %tmp1, 400-%tmp1
amsp SP_C, %tmp1, %tmp2
vsp SP_C, 1
return
*show_dgm
getparam $sarg0
load_dgm $sarg0
wait_dgm
return
*load_dgm
getparam $sarg0
csp SP_DGM0
lsp SP_DGM0, $sarg0, 200, 200
print E_FAST
return
*wait_dgm
getspmode %tmp1, SP_R
getspmode %tmp2, SP_L
textoff
vsp SP_R, 0
vsp SP_L, 0
print E_FAST
click
vsp SP_R, %tmp1
vsp SP_L, %tmp2
print E_FAST
texton
return
*set_vol
fileexist %adv_vol,"suzuka.ars"
if %adv_vol == 0 filecreate "suzuka.ars"
csvopen "suzuka.ars", "rc"
csvread %adv_vol
csvclose
itoa $sadv_vol, %adv_vol
input $sadv_vol, "音量を0~100の間で半角文字で入力してください(0=消音、100=最大)", $sadv_vol, 3, 0
atoi %adv_vol, $sadv_vol
if %adv_vol > 100 mov %adv_vol, 100
if %adv_vol < 0 mov %adv_vol, 0
bgmvol %adv_vol
csvopen "suzuka.ars", "wc"
csvwrite %adv_vol
csvclose
return
*get_vol
fileexist %adv_vol,"suzuka.ars"
if %adv_vol == 0 filecreate "suzuka.ars"
csvopen "suzuka.ars", "rc"
csvread %adv_vol
csvclose
bgmvol %adv_vol
return
*story01
mov %adv_noroi_off, 0
stop
csp SP_R : csp SP_L : print E_FAST
print E_FAST
mov %adv_clear, 0
textclear
bgm ms_narumi
bg "img/blackboard.bmp", E_FAST
talk_mode
r_in ari_n
アリサ「アリサ参上!」\
l_in suzu_n
l_show suzu_dere
すずか「!? アリサちゃん、参上だなんて、
    2007年頃の仮面ラ○ダーじゃある
    まいし、恥ずかしいよ」\
r_show ":l;img/arin.bmp"
アリサ「ありーん」\
;; lsp SP_DGM0, ":l;img/dgm0201a.bmp", 200, 200
アリサ「別にそんなことどうでもいいのよ」\
l_show suzu_exc
すずか「うわ、なんかちっちゃくなった!」\
r_show ari_n
アリサ「ちっちゃいといえば、プログラミング
    言語の#FFFF33Lisp#FFFFFFよね」\
l_show suzu_nc
すずか「Lispといったら、一つのプログラ
    ミング言語を指すんじゃなくて、複数
    の言語の総称なんだよね」\
アリサ「Lispは、MITを始め、いろんな
    ところが独自の拡張を加えて、いろん
    な#FFFF33方言#FFFFFFがあるの」\
すずか「#FFFF33CommonLisp#FFFFFFとか#FFFF33elisp#FFFFFF
    とか、何とかLispっていうのが、
    たくさんあるんだよね」\
アリサ「方言はたくさんあるけど、核となる部
    分はいずれもシンプルで、小さいけど
    非常に強力な言語なのよ」\
すずか「機能をおさえたら#FFFF33NScripter#FFFFFF
    に組み込めるくらいだからね」\
csp SP_R : csp SP_L : print E_VFAST
bg "img/title#01.bmp", 1
wait 1000
delay 3000
bgm ms_setsume
bg "img/blackboard.bmp", E_FAST
speak_mode
r_show ari_n
アリサ「というわけで、Lispの簡単な文法を学んでいきましょう」\
r_show suzur_hrt
すずか「理由がさっぱり分からないけど、とりあえずそれはいいね」\
r_show ari_n
アリサ「Lispで足し算は
#33FF33(+ 1 2)#FFFFFF
のように書くの。@/
インタプリタの対話画面で、これを入力すると、1足す2の答え、3が表示されるわ」\
r_show suzur_nc
すずか「なんかイメージがつかめないよ」\
r_show ari_n
アリサ「そんなときは右クリックでメニューを開いて『#FFFF33インタプリタを起動#FFFFFF』を選択して、/
#33FF33(+ 1 2)#FFFFFFを入力してみるといいわ。/
入力は全て半角文字。Enterキーを押したら結果が出るからね。」\
r_show suzur_nc
すずか「……すこし分かったかも。@まず括弧から始まって、プラスの記号、足される数、足す数、そして最後に括弧だね」\
r_show ari_n
アリサ「そういうこと。引き算の場合は
#33FF33(- 5 3)#FFFFFF
と書くの。#33FF33+#FFFFFFが#33FF33-#FFFFFFに変わっただけで、あとは足し算の場合といっしょね。5引く3が計算されるわ」\
r_show suzur_n
すずか「先に+や-といった演算子を書くっていうのがポイントだね」\
r_show ari_ase
アリサ「……すずか、小学生は『演算子』とか言わないから」\
r_show suzur_dot
すずか「そんなことより、実際にちょっとやってみたいな」\
r_show ari_n
アリサ「わかった。先に少し注意しておくわよ。掛け算は#33FF33*#FFFFFFを使って、割り算も#33FF33/#FFFFFFを使って同じように書けるけど、/
割り算は『余りを無視する』ようになってるから。@/
r_show ari_tun
Lispの尊厳のためにいっておくけど、本当はいまどきのLispは/
複素数だって標準で使えるくらいなの。@NScLisperが整数しか扱えないのは単にサボっただけなんだからね!」\
r_show suzur_ase
すずか「……アリサちゃん、小学生は『複素数』とか『整数』とか言わないよ」\
r_show ":l;img/arin.bmp"
アリサ「ありーん」\
r_show ari_n
アリサ「あと、入力は全て半角文字。Enterキーを押したら結果が出るからね。@/
変な式を打ち込まないように気をつけなさいよ♪」\
gosub *ex_init
*ex01
mov $problem_label, "*ex01_problem"
mov $tab_label, "*ex01_end"
mov $ret_label, "*ex01"
mov $suc_label, "*ex01_check"
goto *ex_rep
*ex01_problem
!s0アリサ「気が済んだらTabキーを押して終わってね」!sd
return
*ex01_check
gosub *pop : gosub *pop
inc %adv_tmp
goto *ex01
*ex01_end
mov %adv_rmode, 1 ;右クリック有効
gosub *pop
saveon
mov %save_flag, 1 ;セーブ可能
textclear
bgm ms_setsume
if %adv_tmp == 1 goto *s_01_2
if %adv_tmp == 0 goto *s_01_1
アリサ「まあ、そんなところね」\
goto *s_01_3
*s_01_1
r_show ":l;img/arin.bmp"
アリサ「ありーん」\
アリサ「一回も試さないってどういうことよ……」\
goto *s_01_3
*s_01_2
アリサ「一回で大丈夫なの? まあ、べつにいいけど……」\
goto *s_01_3
*s_01_3
r_show suzur_qes
すずか「さっき出てきた#33FF33(+ 1 2)#FFFFFFとかって結局なんだったの?」\
r_show ari_n
アリサ「あれこそが、Lispのプログラムよ。それを実際に計算することを、他のプログラミング言語なら『プログラムを実行する』っていう場合が多いけど、/
Lispの場合は#FFFF33式を評価する#FFFFFFというの。@/
#FFFF33式#FFFFFFっていうのは、#33FF33(+ 1 2)#FFFFFFのような形式で書かれたもの。/
#FFFF33評価#FFFFFFっていうのはある規則にそって式から何らかの値を計算することね」\
r_show suzur_n
すずか「計算結果……というより、評価した結果っていうのかな?@ それが画面に表示されてたのはなんで? /
表示するような命令を書いた覚えはないんだけど」\
r_show ari_n
アリサ「式を評価した結果のことは#FFFF33式の値#FFFFFFというわ。@/
Lispインタプリタは、入力された式の値を#FFFF33自動的に画面に表示#FFFFFFすることになってるのよ。@/
だから、何も書かなくても結果が画面に表示されたわけ。@/
Lispインタプリタは、『読み込み』『評価』『表示』の三つの動作から成り立つの。@/
これを『read-evaluate-print loop』といったり、/
トップレベルといったりするわ」\
r_show suzur_nc
すずか「話をまとめると、Lispインタプリタでプログラムを動かすっていうのは、まず、式を入力する。@/
インタプリタはそれを読み込んで、@評価して、@結果を表示する。こういう流れだっていうことだね」\
r_show ari_n
アリサ「それから、#33FF33(* 3 4)#FFFFFFのようなもの全体のことを式というけど、これを構成する、/
#33FF33+#FFFFFFや#33FF331#FFFFFFや#33FF332#FFFFFFも、それぞれ式なの。@/
式に含まれている式であることを強調する時は#FFFF33部分式#FFFFFFといったりもするわ」\
r_show suzur_n
すずか「つまり、#33FF33(* 3 4)#FFFFFFは、3つの式を括弧で括って並べたものなんだね。@/
それから、一つ一つの式は空白をいれて区切るんだね」\
r_show ari_n
アリサ「括弧の中で最初に現れる式を#FFFF33演算子#FFFFFF、それ以外の式を#FFFF33被演算子#FFFFFFというわ。@/
show_dgm ":l;img/dgm0101.bmp"
#33FF33(* 3 4)#FFFFFFの場合、#33FF33*#FFFFFFが演算子、#33FF333#FFFFFFと#33FF334#FFFFFFが被演算子ね」\
r_show suzur_n
すずか「括弧で括られた式は、演算子と被演算子に分けられて、@/
演算子に被演算子を作用させたものがその式の結果になるんだね」\
r_show ari_n
アリサ「これから時々練習問題をやっていくけど、それ以外のどんなタイミングでも、/
右クリックでメニューを開いて『#FFFF33インタプリタを起動#FFFFFF』を選択すれば、/
いつでも式を打ち込むことができるからね」\
r_show suzur_n
すずか「説明の途中で色々確認するために使うと便利そうだね」\
csp SP_DGM0
csp SP_R
bg black, E_NORMAL
r_load ari_n
l_load suzu_n
bgm ms_after
bg "img/blackboard.bmp", E_NORMAL
talk_mode
アリサ「なのはとユーノの情報によると魔法も
    プログラム仕掛けらしいの。つまりプ
    ログラミングさえできれば……」\
すずか「いずれ、私たちも魔法の力をこの手に
    できる。時空管理局の無能な人たちが
    偉そうな顔をするのもおしまい」\
r_show ari_hrt
アリサ「春休みの間に魔法の力を手に入れるっ
    てのも悪くないでしょ」\
l_show suzu_nc
すずか「それは有意義な春休みだね」\
r_show ari_n
アリサ「ふふ、!w300ふふふ、!w200あはは、!w100
    あーはっはっはっは!!!」\
;;;;;第1話終了;;;;;
speak_mode
textclear
vsp SP_R, 0
vsp SP_L, 0
bg black, E_SLOW
;;共通記録の書き換え
csvopen "arisa.szk", "rc"
csvread %adv_clear
csvclose
if %adv_clear >= 1 goto *story01_after
mov %adv_clear, 1
csvopen "arisa.szk", "wc"
csvwrite %adv_clear
csvclose
#FFFF33用語集#FFFFFFに単語が追加されました。\
*story01_after
r_show suzur_nc
すずか「どうしようかな?」
csel "補足説明を読む", *story01_condicil, "用語集を読む", *story01_glossary, "タイトルに戻る", *title_back, "第2話へ進む", *story02
*story01_condicil
gosub *condicil
goto *story01_after
*story01_glossary
gosub *glossary
goto *story01_after
;;;;;第2話;;;;;
*story02
mov %adv_noroi_off, 0
csp SP_R : csp SP_L : print E_FAST
stop
talk_mode
bgm ms_narumi
bg "img/blackboard.bmp", E_FAST
r_in ari_n
l_in suzu_n
talk_mode
l_show suzu_exc
すずか「アリサちゃん『まめでがんす』ってい
    ってみて」\
r_show ari_ase
アリサ「何よ突然……というより、一体何よそ
    れ?」\
l_show suzu_n
すずか「『まめでがんす』っていうのは広島の
    方言で『元気です』という意味だよ」\
r_show ari_qes
アリサ「そうなの?」\
l_show suzu_qes
すずか「あれ? アリサちゃんが昔そういって
    なかったっけ」\
アリサ「いったかしら……方言といえば、なに
    か大事なことを忘れていたような気が
    するわね」\
csp SP_R : csp SP_L : print 1
bg "img/title#02.bmp", E_VFAST
wait 1000
delay 2000
bgm ms_setsume
bg "img/blackboard.bmp", E_FAST
speak_mode
r_show ari_n
アリサ「そうそう。Lispのどの#FFFF33方言#FFFFFFを使うか決めないといけないわね」\
r_show suzur_nc
すずか「えーと、Lispはいろんなところが独自拡張とかをしてきたからいろんなルールがあるんだよね」\
r_show ari_n
アリサ「ここでは、#FFFF33Scheme#FFFFFFと呼ばれる方言に似せたものを使うことにしましょう。Schemeはコンパクトな仕様で非常に美しいのよ」\
r_show suzur_n
すずか「まあ、文法をSchemeライクにしただけで、NScLisperは/
その『コンパクトな仕様』さえも、満たしてないんだよね」\
r_show ":l;img/arin.bmp"
アリサ「ありーん」\
r_show ari_n
アリサ「それはさておき、今回は変数を使ってみましょうか。@
#33FF33(define x 128)#FFFFFF
この式を評価すると、Lispインタプリタの#FFFF33環境#FFFFFFというモノに『変数xは128』と記憶されるの。@/
show_dgm ":l;img/dgm0201a.bmp"
それで、この式を評価した後に式#33FF33x#FFFFFFを評価したら、結果は128になるのよ。@/
show_dgm ":l;img/dgm0201b.bmp"
変数を評価した結果のことを#FFFF33変数の値#FFFFFFというわ」\
csp SP_DGM0
r_show suzur_n
すずか「つまり、#33FF33(define 変数名 式)#FFFFFFを評価すると、変数を定義できるんだね。@/
あと、変数を評価した結果のことは#FFFF33変数の値#FFFFFFっていうんだ」\
r_show ari_n
アリサ「括弧で囲まれたものだけじゃなくて、xとか128とかも式だってことを忘れないでね。@/
あと、defineで定義していない変数を評価するとエラーになるわ。/
たとえば、変数yを定義していないときに評価すると、#33FF33yは未束縛#FFFFFFというエラーが表示されるの」\
r_show suzur_dere
すずか「定義していない変数を使うとエラーになるんだね」\
r_show ari_n
アリサ「ちなみに、数を表す式を直接評価――例えば、#33FF33256#FFFFFFとかを評価したらどうなると思う?」\
r_show suzur_qes
すずか「えーと、256以外にはなれそうもないし、256のままなのかな?」\
r_show ari_n
アリサ「その通り。一見当たり前のような規則だけど、これが案外重要なの。@/
さっきのdefineの式を評価した後に、
#33FF33(+ x 896)#FFFFFF
を評価するとどうなると思う?」\
r_show suzur_nc
すずか「えっと、128足す896が計算されて1024になるんじゃないかな?」\
r_show ari_n
アリサ「式の値はその通りよ。@/
けど、流れとしては、#33FF33(+ x 896)#FFFFFFを評価する際に、/
まず、xが評価されて、128になるの。@
その後さらに、896も評価されて896になってから+による足し算が行われるの。@/
show_dgm ":l;img/dgm0202.bmp"
このように、被演算子は先に評価されるのよ」\
csp SP_DGM0
r_show suzur_exc
すずか「#33FF33(+ A B)#FFFFFFは+が作用するよりも先に、AとBが評価されるんだね」\
r_show ari_n
アリサ「そう。そして、AやBは数や変数じゃなくて、括弧で囲われた式でもいいの。つまり、
#33FF33(+ (* 3 4)(- 5 2))#FFFFFF
みたいな式を書くことも許されるわ」\
r_show suzur_excc
すずか「演算子が被演算子に作用するよりも先に被演算子は評価されるんだね。@/
だから、+とかの演算子が作用する被演算子は数であっても、括弧で囲われた式であっても、それを評価した結果が数であればいいのかな」\
csp SP_DGM0
r_show ari_ase
アリサ「……妙に理解が早いわね。じゃあ、実際に何かやってみましょうか。@/
r_show ari_exc
今回も演習の前に諸注意。/
#33FF33(define name 2)#FFFFFFを評価すると#33FF33name#FFFFFFという結果になるわ。@/
少々不思議に感じるかもしれないけど、これが一体何なのかは/
持ち越しにするから、今はあまり深く考えたら駄目だからね!@/
 あと、どうしても駄目だと思ったら#FFFF33Tab#FFFFFFキーを押すといいことが起こるかもしれないわ」\
gosub *ex_init
*ex02
mov $problem_label, "*ex02_problem"
mov $tab_label, "*ex02_tab"
mov $ret_label, "*ex02"
mov $suc_label, "*ex02_check"
goto *ex_rep
*ex02_problem
!s0アリサ「まず、xの値を4と定義しなさい」!sd
return
*ex02_check
gosub *pop : gosub *pop
;答え合わせ
mov $sarg0, "x"
gosub *create_symbol
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *find_var
mov %arg0, %ret
gosub *cdr
mov %arg0, %ret
gosub *get_tag
mov %tmp1, %ret
gosub *get_data
mov %tmp2, %ret
mov $sarg0, "*ex02_2"
if %tmp1 == TAG_NUM && %tmp2 == 4 goto *ex_check_pass ;S(new env)
mov $sarg0, "*ex02_normal_miss"
goto *ex_check_fail
*ex02_normal_miss
r_show ari_die
アリサ「……なに違うことやってるのよ。やり直し!」\
r_show ari_n
inc %adv_tmp
gosub *pop ;new env<S()
gosub *ex_init_env : goto *ex02
*ex02_2 ;S(new env)
mov $problem_label, "*ex02_2_problem"
mov $tab_label, "*ex02_2_tab"
mov $ret_label, "*ex02_2"
mov $suc_label, "*ex02_2_check"
goto *ex_rep
*ex02_2_problem
!s0アリサ「次に、(x+3)*4を計算しなさい」!sd
return
*ex02_2_check
gosub *pop ;new object<S(input, new env)
mov %arg0, %ret
gosub *get_tag
mov %tmp1, %ret
gosub *get_data
mov %tmp2, %ret
; Don't use ex_check_pass for ex02_2_check2
if %tmp1 == TAG_NUM && %tmp2 == 28 goto *ex02_2_check2 ;この後xの値を変えてチェック
;ここでxの再定義をしてくれるという素晴らしい人のために
mov $sarg0, "x"
gosub *create_symbol
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *find_var
mov %arg0, %ret
gosub *cdr
mov %arg0, %ret
gosub *get_tag
mov %tmp1, %ret
gosub *get_data
mov %tmp2, %ret
mov $sarg0, "*ex02_ext"
if %tmp1 != TAG_NUM goto *ex_check_fail
if %tmp2 != 4 goto *ex_check_fail
mov $sarg0, "*ex02_2_normal_miss"
goto *ex_check_fail
*ex02_2_normal_miss
r_show ari_die
アリサ「違うわよ。もう一回やり直し!」\
r_show ari_n
gosub *pop ;input<S(new env)
inc %adv_tmp
goto *ex02_2
*ex02_ext
r_show ari_muka
アリサ「……なかなか面白いことをやってくれるわね。@/
r_show ari_do
最初からやり直し!!!」\
r_show ari_n
gosub *pop ;input<S(new env)
gosub *pop ;new env<S()
inc %adv_tmp
gosub *ex_init_env : goto *ex02
*ex02_2_check2 ;(x+3)*4なのかチェック
mov $sarg0, "(set! x 997)"
gosub *input_to_lobject
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *eval_form ;xの値を997に書き換え
gosub *pop ;input<S(new env)
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *eval_form
mov %arg0, %ret
gosub *get_tag
mov %tmp1, %ret
gosub *get_data
mov %tmp2, %ret
mov $sarg0, "*ex02_end"
if %tmp1 == TAG_NUM && %tmp2 == 4000 goto *ex_check_pass_end
r_show ari_muka
アリサ「あのさ、なんで答えだけがあってるのかな……@/
r_show ari_do
最初からやり直し!!!」\
r_show ari_n
gosub *pop ;new env<S()
inc %adv_tmp
gosub *ex_init_env
mov $sarg0, "*ex02"
goto *ex_check_fail
*ex02_tab
csel "続ける", *ex02, "ヒント", *ex02_hint, "タイトルに戻る", *ex02_bye
*ex02_hint
textclear
アリサ「変数の定義は
#33FF33(define 変数名 式)#FFFFFF
よ」\
goto *ex02
*ex02_bye
mov %adv_rmode, 1 ;右クリック有効
textclear
csp SP_R
stop
goto *title
*ex02_2_tab
csel "続ける", *ex02_2, "ヒント", *ex02_2_hint, "タイトルに戻る", *ex02_2_bye
*ex02_2_hint
textclear
アリサ「まず、『(x+3)*4』はx+3と4の積よね。@/
この2つはそれぞれ#33FF33(+ x 3)#FFFFFFと#33FF334#FFFFFFという式で表せるわ。@/
それから、被演算子が先に評価されるという規則があるから、式Aと式Bの積は
#33FF33(* A B)#FFFFFF
という式になるわ。この問題の場合は式AとBは既に言った2つの式だから……」\
goto *ex02_2
*ex02_2_bye
mov %adv_rmode, 1 ;右クリック有効
textclear
csp SP_R
stop
gosub *pop
goto *title
*ex02_end
mov %adv_rmode, 1 ;右クリック有効
gosub *pop ;new env<S()
saveon
mov %save_flag, 1 ;セーブ可能
add %adv_miss, %adv_tmp
bgm ms_setsume
textclear
if %adv_tmp == 0 goto *s_02_2
if %adv_tmp >=3 goto *s_02_3
*s_02_1
アリサ「まあまあだったわね」\
goto *s_02_4
*s_02_2
r_show ari_hrt
アリサ「全く問題ないようね」\
goto *s_02_4
*s_02_3
r_show ari_ase
アリサ「ちょっと失敗が多いわね」\
goto *s_02_4
*s_02_4
r_show suzur_qes
すずか「結局、変数ってなんだったのかな?」\
r_show ari_n
アリサ「変数というのは、文字の並びで構成される#FFFF33名前#FFFFFFが#33FF33値#FFFFFFと結びついたものなの。@/
値というものは、今のところは7とか8といった数を思い浮かべておくといいわ」\
r_show suzur_nc
すずか「評価したら、結果が128になるような変数xっていうのは、/
『128という値』と結びついた『xという名前』なのかな」\
r_show ari_n
アリサ「これまでの説明では#33FF33x#FFFFFFとか、簡素な名前しか使わなかったけど@/
#33FF33suzuka#FFFFFFとか#33FF33nanoha19#FFFFFFとか、/
アルファベットと数字で構成されたり、@#33FF33starlight-breaker!#FFFFFFとか、/
記号を混ぜることもできるのよ」\
r_show suzur_n
すずか「記号を使えるといっても、括弧『(』『)』とかは使用できないし、@#33FF33666#FFFFFFのように/
数字だけで構成されるものは#FFFF33数#FFFFFFとして扱われるから#FFFF33名前#FFFFFFには出来ないってことだね」\
r_show ari_hrt
アリサ「さすがね。よく分かってるじゃない!@/
 あと、普通はLispは大文字と小文字を区別しないことが多いけど、/
NScLisperでは区別するから注意してね。@この#FFFF33名前#FFFFFFをLispでは#FFFF33シンボル#FFFFFFというものを使って表すの」\
r_show suzur_qes
すずか「変数っていうのは、値と結びついた#FFFF33シンボル#FFFFFFなんだね。じゃあ、#FFFF33変数の定義#FFFFFFとか/
#FFFF33変数の評価#FFFFFFってのはなんだったのかな?」\
r_show ari_n
アリサ「Lispにおける変数の定義っていうのは、#FFFF33シンボル#FFFFFFと#FFFF33値#FFFFFFを結びつけることなのよ。@/
例えば、#33FF33(define x 16)#FFFFFFはxという名前と16という値を結びつけるの。/
このことを『xが16に#FFFF33束縛(bind)#FFFFFFされる』というの」\
r_show suzur_qes
すずか「xが16を束縛するんじゃなくて、xが16に束縛されるの?」\
r_show ari_n
アリサ「日本語として微妙な言い回しかもしれないけど、英語の本とかだとそう書いてたからきっとそうなのよ@/
r_show ari_ase
……多分」\
r_show suzur_n
すずか「確か#FFFF33環境#FFFFFFっていうモノに、変数と値の結びつきが入っているんだよね?」\
r_show ari_n
アリサ「環境っていうのはシンボルと値の結びつきである#FFFF33束縛#FFFFFFの集まりなの。/
defineを使うと、この環境に新たな束縛を加えることが出来るの。@/
そして、変数を評価すると環境からその名前が探されて、それに対応する値が結果となるのよ」\
r_show suzur_qes
すずか「ねえねえ、ふと疑問に思ったんだけど、#33FF33(+ x 1)#FFFFFFは、#33FF33x#FFFFFFが評価されてから、/
#33FF33+#FFFFFFがそれに作用するんだよね。@けど、#33FF33(define x 32)#FFFFFFと書いた場合は?@ /
show_dgm ":l;img/dgm0203.bmp"
これは、xは評価されずにシンボルとして扱われてない?」\
r_show ari_n
アリサ「いいところに目を付けたわね。/
defineの2つ目の被演算子は評価されるけど、1つ目の被演算子は評価せずにシンボルとして扱われるわ。@/
こうなる理由は、今は『defineは特別だから』としか言えないわね」\
csp SP_DGM0
r_show suzur_ase
すずか「えー、そんなのずるいよアリサちゃん。教えてよ~」\
csp SP_R
bg black, E_NORMAL
r_load ari_n
l_load suzu_n
bgm ms_after
bg "img/blackboard.bmp", E_NORMAL
talk_mode
アリサ「まあまあ、そんなに焦らなくても。大
    丈夫よ。この調子だとすぐに魔法も使
    えるようになるはずよ」\
l_show suzu_qes
すずか「魔法を使うには、生まれながらの資質
    も必要だってなのはちゃんから聞いた
    けど、私たちはどうなんだろうね」\
r_show ari_tun
アリサ「そればっかしは私たちにはどうなのか
    分からないわね」\
l_show suzu_ase
すずか「……それは大問題じゃないの?」\
r_show ari_n
アリサ「けど、たとえ魔法の資質がなくても、
    魔法の効果を作り出すプログラミング
    さえできれば十分よ」\
l_show suzu_n
すずか「杖(デバイス)に魔法のプログラムを
    保存して、それを魔法が使える人に持
    たせたらいいんだよね」\
アリサ「そうすると、魔法を発動させることが
    できるはずよ。だから私たちはプログ
    ラミングに専念しましょう」\
l_show suzu_nc
すずか「もし、私たち二人ともが魔法が使えな
    かった時は、魔法を使える人を探さな
    いとね」\
r_show ari_tun
アリサ「その時は鮫島に任せましょう。あいつ
    なら、多分大丈夫よ@/
r_show ari_dere
……年齢的に」\
l_show suzu_dot
すずか「まさか、アリサちゃん、25歳を過ぎ
    たら魔法を使えるっていうのを信じて
    たりしないよね」\
アリサ「……大丈夫。きっともう一つの条件も
    クリアしてるわ」\
l_show suzu_dotc
すずか「アリサちゃん……」\
;;;;;第2話終了;;;;;
speak_mode
textclear
vsp SP_R, 0
vsp SP_L, 0
bg black, E_SLOW
;共通記録の書き換え
csvopen "arisa.szk", "rc"
csvread %adv_clear
csvclose
if %adv_clear >= 2 goto *story02_after
mov %adv_clear, 2
csvopen "arisa.szk", "wc"
csvwrite %adv_clear
csvclose
#FFFF33補足説明#FFFFFFが追加されました。
#FFFF33用語集#FFFFFFに単語が追加されました。\
*story02_after
r_show suzur_nc
すずか「どうしようかな?」
csel "補足説明を読む", *story02_condicil, "用語集を読む", *story02_glossary, "タイトルに戻る", *title_back, "第3話へ進む", *story03
*story02_condicil
gosub *condicil
goto *story02_after
*story02_glossary
gosub *glossary
goto *story02_after
;;;;;第3話;;;;;
*story03
mov %adv_noroi_off, 0
csp SP_R : csp SP_L : print E_FAST
stop
talk_mode
bgm ms_narumi
bg "img/blackboard.bmp", E_FAST
r_in ari_n
l_in suzu_n
アリサ「有名な『鉄腕アトム』に登場するアト
    ムの脳の記憶容量って15兆8千億ビ
    ットなのよ。知ってた?」\
l_show suzu_exc
すずか「そうなんだ。@/
l_show suzu_ase
でも、あれ……これって
    計算すると2テラバイトより少ないこ
    とになるよ。案外少ないね」\
csp SP_R : csp SP_L : print 1
bg "img/title#03.bmp", E_VFAST
wait 1000
delay 2000
bgm ms_setsume
bg "img/blackboard.bmp", E_FAST
speak_mode
r_show ari_n
アリサ「というわけで、数とシンボルの話をしましょう」\
r_show suzur_T_T
すずか「アトムの話はどこにいったの……?」\
r_show ari_n
アリサ「括弧で囲まれた式全体を#FFFF33リスト#FFFFFFというの。@/
それから、括弧で囲われてない式(シンボルや数)を#FFFF33アトム#FFFFFFというの。@/
show_dgm ":l;img/dgm0301.bmp"
Lispの少し古い言い方ではそれぞれを、文字アトムとか、数アトムとか言ってたんだけど、/
最近はあまりこの言い方は使われてないようね」\
csp SP_DGM0
r_show suzur_ase
すずか「それが言いたかっただけの理由でアトムの話を出したんだ」\
r_show ":l;img/arin.bmp"
アリサ「ありーん」\
r_show suzur_nc
すずか「数は評価しても変化しないけど、シンボルは評価したら別のものに変わっちゃうんだよね。/
これってなんか不公平な気がするな」\
r_show ari_n
アリサ「ふっふっふ……Lispに抜かりはないわ。
#33FF33’suzuka#FFFFFF
これを評価すると、結果は#33FF33suzuka#FFFFFFになるのよ」\
r_show suzur_n
すずか「どうして私の名前を使うかなぁ……じゃなくて、文字の並びが結果になってるよ。/
これはひょっとしてシンボルのなの?」\
r_show ari_exc
アリサ「ご名答。#33FF33’suzuka#FFFFFFっていうのは正確には/
#33FF33(quote suzuka)#FFFFFFの省略した記法で、こう書いても同じことになるわ」\
r_show suzur_n
すずか「つまり、#33FF33(quote symbol)#FFFFFFを評価したら、/
#33FF33symbol#FFFFFFがそのまま結果になるってことだね」\
r_show ari_n
アリサ「quoteがsymbolに作用する前にsymbolが評価されないのか、って思うかもしれないけど、/
これは『quoteが特別だから』評価されないのよ」\
r_show suzur_qes
すずか「defineも特別って言ってたけど、これは/
#33FF33(define name expr)#FFFFFFを評価すると、/
exprは評価されるけど、nameは評価されず、そのままのシンボルとして扱われるからだったよね。@/
つまり、演算子が特別だと、評価されない被演算子があるってこと?」\
r_show ari_n
アリサ「その通り。よく分からないとしても、#33FF33’name#FFFFFFを評価すると、/
#33FF33name#FFFFFFが結果として返るということだけは覚えてね。@/
ちなみに、式#33FF33’3#FFFFFFの値は#33FF333#FFFFFFになるわ。/
余裕があるならこれも覚えておくといいわね」\
r_show suzur_qes
すずか「ねえ、シンボルといえば、前は変数の名前みたいな感じで出てきたけど、今回は違うの?」\
r_show ari_exc
アリサ「ここは少し難しいかもしれないから注意して聞いてね。@/
#33FF3319#FFFFFFとか#33FF33nanoha#FFFFFFといったものは、共に#FFFF33値#FFFFFF(オブジェクト)と呼ばれるの。@/
値と一言に言っても、数字の並びから出来ているものや、文字の並びから出来ているものなど、種類がいくつかあるの。@/
『数』『シンボル』といった値の種類のことを#FFFF33型#FFFFFFというわ」\
r_show suzur_excc
すずか「#33FF3319#FFFFFFは型が数である値。#33FF33nanoha#FFFFFFは型がシンボルである値ってことだね@/
show_dgm ":l;img/dgm0302.bmp"
」\
csp SP_DGM0
r_show ari_n
アリサ「型が数である値を単に『数』と呼んだり、型がシンボルである値を単に『シンボル』といったりするけど、/
これらは値という言葉を省略しているのよ」\
r_show suzur_nc
すずか「そういえば、変数は#FFFF33束縛されたシンボル#FFFFFFだったね。逆に言うと、/
束縛されてないシンボルっていうものもあるんだよね」\
r_show ari_n
アリサ「シンボルは評価されると、環境から対応する値が探されて、それが結果となるの。@/
けど、評価さえしなければ、それは文字の並びを表すシンボルとして、そのまま残るわ@/
show_dgm ":l;img/dgm0303.bmp"
」\
csp SP_DGM0
r_show suzur_dotc
すずか「えーと、#33FF33’symbol#FFFFFFはsymbolはquoteの効果で評価されないから……@/
r_show suzur_exc
そっか、だから引用符’でシンボルを作れるんだね」\
r_show ari_n
アリサ「さらにさらに。シンボルも値なんだから変数に対応する値として環境に束縛を加えることも出来るのよ。/
#33FF33(define nanoha ’fate)#FFFFFFを評価して、/
#33FF33nanoha#FFFFFFを評価すると、結果は#33FF33fate#FFFFFFになるわ」\
r_show suzur_excc
すずか「なのはちゃんがフェイトちゃんに束縛されてることに注意@/
r_show suzur_ase
……じゃなくて、#33FF33(define 変数名 式)#FFFFFFの/
#33FF33式#FFFFFFは評価されるから、シンボルにしたかったら、引用符’をつける必要があるけど、@/
#33FF33変数名#FFFFFFはdefineの効果で、元々評価されないから引用符’を付けたら駄目なことに注意だね」\
r_show ari_n
アリサ「色々頭の中がごちゃごちゃしてきたと思うから、簡単な問題をやってみましょう」\
gosub *ex_init
*ex03
mov $problem_label, "*ex03_problem"
mov $tab_label, "*ex03_tab"
mov $ret_label, "*ex03"
mov $suc_label, "*ex03_check"
goto *ex_rep
*ex03_problem
!s0アリサ「結果が『#33FF33hayate#FFFFFF』というシンボルになる式を評価しなさい」!sd
return
*ex03_check
mov $sarg0, "hayate"
gosub *create_symbol
mov %tmp, %ret
gosub *pop ;ret<S(input)
mov $sarg0, "*ex03_2"
if %tmp == %ret gosub *pop : goto *ex_check_pass
mov $sarg0, "*ex03_normal_miss"
goto *ex_check_fail
*ex03_normal_miss
gosub *pop
textclear
r_show ari_die
アリサ「違うわ。やり直し!」\
r_show ari_n
inc %adv_tmp
goto *ex03
*ex03_2
mov $problem_label, "*ex03_2_problem"
mov $tab_label, "*ex03_2_tab"
mov $ret_label, "*ex03_2"
mov $suc_label, "*ex03_2_check"
goto *ex_rep
*ex03_2_problem
!s0アリサ「変数fateを『nanoha』というシンボルが値となるように定義しなさい」!sd
return
*ex03_2_check
gosub *pop : gosub *pop
mov $sarg0, "fate"
gosub *create_symbol
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *find_var
mov %arg0, %ret
gosub *cdr
mov %arg0, %ret
gosub *push ;S(value of fate)
mov $sarg0, "nanoha"
gosub *create_symbol
mov %arg0, %ret
gosub *pop ;value of fate<S()
mov $sarg0, "*ex03_3"
if %arg0 == %ret goto *ex_check_pass
mov $sarg0, "*ex03_2_normal_miss"
goto *ex_check_fail
*ex03_2_normal_miss
textclear
r_show ari_die
アリサ「違うわ。やり直し!」\
r_show ari_n
inc %adv_tmp
goto *ex03_2
*ex03_3
mov $problem_label, "*ex03_3_problem"
mov $tab_label, "*ex03_3_tab"
mov $ret_label, "*ex03_3"
mov $suc_label, "*ex03_3_check"
goto *ex_rep
*ex03_3_problem
!s0アリサ「変数fateを評価しなさい」!sd
return
*ex03_3_check
;;;定義の書き換えcheck;;;
mov $sarg0, "fate"
gosub *create_symbol
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *find_var
mov %arg0, %ret
gosub *cdr
mov %arg0, %ret
gosub *push ;S(value of fate, ret, input)
mov $sarg0, "nanoha"
gosub *create_symbol
mov %arg0, %ret
gosub *pop ;value of fate<S(ret, input)
mov $sarg0, "*ex03_3_miss2"
if %arg0 != %ret sub %sp, 2 : goto *ex_check_fail
;;;end;;;
mov $sarg0, "nanoha"
gosub *create_symbol
mov %arg0, %ret
gosub *pop ;ret<S(input)
mov $sarg0, "*ex03_3_miss"
if %arg0 != %ret gosub *pop : goto *ex_check_fail
mov $sarg0, "fate"
gosub *create_symbol
mov %arg0, %ret
gosub *pop ;input<S()
mov $sarg0, "*ex03_end"
if %arg0 == %ret goto *ex_check_pass_end
mov $sarg0, "*ex03_3_miss"
goto *ex_check_fail
*ex03_3_miss
textclear
r_show ari_die
アリサ「違うわ。やり直し!」\
r_show ari_n
inc %adv_tmp
goto *ex03_3
*ex03_3_miss2
textclear
r_show ari_muka
アリサ「……最初からやり直し!!」\
r_show ari_n
inc %adv_tmp
gosub *pop ;new env
gosub *ex_init_env : goto *ex03
*ex03_tab
csel "続ける", *ex03, "ヒント", *ex03_hint, "タイトルに戻る", *ex03_bye
*ex03_2_tab
csel "続ける", *ex03_2, "ヒント", *ex03_2_hint, "タイトルに戻る", *ex03_bye
*ex03_3_tab
csel "続ける", *ex03_3, "ヒント", *ex03_3_hint, "タイトルに戻る", *ex03_bye
*ex03_bye
mov %adv_rmode, 1 ;右クリック有効
textclear
csp SP_R
stop
gosub *pop
goto *title
*ex03_hint
textclear
アリサ「シンボルを評価せず、そのままの形で残すには引用符’を使って#33FF33’x#FFFFFFのように書くか、/
#33FF33(quote x)#FFFFFFと書けばいいわ」\
goto *ex03
*ex03_2_hint
textclear
アリサ「変数の定義は今まで通り#33FF33(define 変数名 式)#FFFFFFとすればいいわ。@/
#33FF33変数名#FFFFFFは評価されないけど、#33FF33式#FFFFFFは評価されるから、引用符’を使えばいいわね」\
goto *ex03_2
*ex03_3_hint
textclear
アリサ「何も考えずに、変数名だけを書いたらいいわ」\
goto *ex03_3
*ex03_end
mov %adv_rmode, 1 ;右クリック有効
gosub *pop ;new env<S()
saveon
mov %save_flag, 1 ;セーブ可能
textclear
add %adv_miss, %adv_tmp
bgm ms_setsume
if %adv_tmp == 0 goto *s_03_2
if %adv_tmp >=4 goto *s_03_3
*s_03_1
アリサ「まあまあだったわね」\
goto *s_03_4
*s_03_2
r_show ari_hrt
アリサ「全く問題ないようね」\
goto *s_03_4
*s_03_3
r_show ari_ase
アリサ「ちょっと失敗が多いわね」\
goto *s_03_4
*s_03_4
r_show ari_n
アリサ「quoteや引用符’を使って、式や部分式を評価しないことを#FFFF33quoteする#FFFFFFというの。@/
変な言い回しかもしれないけど覚えてね」\
r_show suzur_qes
すずか「今更だけどさ、+とか-とかdefineとかって一体何なの? 見た目はシンボルのようだけど……」\
r_show ari_n
アリサ「それを知るにはまず、評価のルールを知る必要があるわね。
#33FF33(演算子 被演算子1 被演算子2…被演算子n)#FFFFFF
いままで何度も見てきた括弧で囲われた式ね。被演算子は長いから#FFFF33引数#FFFFFFといいましょう。
一つ目、二つ目の引数のことを第一引数、第二引数という感じに呼ぶことにするわね」\
r_show suzur_nc
すずか「演算子が+や-の時は引数は評価されるけど、quoteとかの場合は特別に評価されないんだよね」\
r_show ari_n
アリサ「+や-のように、引数を評価するものを#FFFF33関数#FFFFFF、@/
defineやquoteのように引数の扱いが特別なものを、/
#FFFF33スペシャルフォーム#FFFFFFというの」\
r_show suzur_T_T
すずか「『特別だから』とさんざん言ってきたものの名前がスペシャルフォームだなんて……@/
そのまま過ぎない?」\
r_show ari_do
アリサ「うるさいうるさいうるさい」\
r_show suzur_dere
すずか「まあまあ、落ち着いて」\
r_show ari_n
アリサ「そうそう、今まで『演算子を被演算子に作用させる』みたいな言い回しを使ってきたけど、/
関数の場合『引数をつけて関数を呼び出す』ということにするわ。@/
それから、#33FF33(関数 引数1…引数n)#FFFFFFのような式を『#FFFF33関数呼び出し#FFFFFF』というわね。@/
あと、関数呼び出しを評価して得られる結果を#FFFF33関数が返す値#FFFFFFと呼ぶことにするわ」\
r_show suzur_qes
すずか「新しい言葉が一杯出てきたけど、+とかが見た目はシンボルのようだっていう話はどうなの?」\
r_show ari_n
アリサ「+や-は、それ自身はシンボルに違いないわ。ただ、#FFFF33型が関数である値#FFFFFFに束縛されているの。/
リストを評価すると引数だけじゃなくて演算子も評価されるから、その値が現れるんだけど、それは難しいからまた今度にしましょう」\
r_show suzur_n
すずか「うーん、なんだか難しい話だね。/
とりあえず、今は#FFFF33スペシャルフォーム#FFFFFF、#FFFF33関数#FFFFFF、#FFFF33引数#FFFFFFという名称だけを覚えて、/
詳しいところはまた話が進んでから理解すれば十分かな」\
vsp SP_R, 0
bg black, E_NORMAL
r_load ari_n
l_load suzu_n
bgm ms_after
bg "img/blackboard.bmp", E_NORMAL
talk_mode
アリサ「なかなか順調にLispに慣れてきて
    いるわね」\
l_show suzu_qes
すずか「アリサちゃん、プログラミング言語っ
    ていろんな種類があるって聞いたけど
    なんでLispなの?」\
アリサ「Lispはね、人工知能の開発に強い
    の。だから人工知能搭載のデバイスの
    開発に使われてる」\
l_show suzu_exc
すずか「なるほど、デバイスの開発に使う標準
    的な言語なんだね」\
r_show ari_qes
アリサ「そう。そして、時空管理局のコンピュ
    ータは『何故か』全てLispマシン
    なのよ」\
l_show suzu_nc
すずか「時空管理局は全面的にLispを使っ
    ているんだね。それはいいけど、そろ
    そろデバイスを手に入れないとね」\
r_show ari_n
アリサ「それは時空管理局で失敬しましょう」\
l_show suzu_dot
すずか「さすがにそれはまずくない? という
    より、そう簡単にいくかな」\
アリサ「大丈夫。実は何日も掛けて計画を練っ
    ていたのよ。近々決行するわよ」\
l_show suzu_dotc
すずか「万が一、局員に見つかった時は?」\
r_show ari_hrt
アリサ「鮫島を連れて行きましょう。何かあっ
    た時に責任を取るのは大人よ。子供は
    取る必要はないわ」\
l_show suzu_ase
すずか「アリサちゃん……酷い……」\
;;;;;第3話終了;;;;;
speak_mode
textclear
vsp SP_R, 0
vsp SP_L, 0
bg black, E_SLOW
;;共通記録の書き換え
csvopen "arisa.szk", "rc"
csvread %adv_clear
csvclose
if %adv_clear >= 3 goto *story03_after
mov %adv_clear, 3
csvopen "arisa.szk", "wc"
csvwrite %adv_clear
csvclose
#FFFF33補足説明#FFFFFFが追加されました。
#FFFF33用語集#FFFFFFに単語が追加されました。\
*story03_after
r_show suzur_nc
すずか「どうしようかな?」
csel "補足説明を読む", *story03_condicil, "用語集を読む", *story03_glossary, "タイトルに戻る", *title_back, "第4話へ進む", *story04
*story03_condicil
gosub *condicil
goto *story03_after
*story03_glossary
gosub *glossary
goto *story03_after
;;;;;第4話;;;;;
*story04
mov %adv_noroi_off, 0
csp SP_R : csp SP_L : print E_FAST
stop
talk_mode
bgm ms_narumi
bg "img/blackboard.bmp", E_FAST
r_in ari_n
l_in suzu_n
アリサ「さあ、今日は時空管理局に潜入するわ
    よ。準備はいい?」\
l_show suzu_dotc
すずか「やっぱり泥棒はいけないと思うな」\
アリサ「大丈夫。なのはに聞いた話によると、
    今、旧式のデバイスが大量に破棄され
    ようとしているらしいの」\
l_show suzu_n
すずか「へー、そうなんだ」\
r_show ari_hrt
アリサ「新型のデバイスより性能は劣るけど、
    まだ使えるのよ。これを捨てるなん
    てもったいないでしょ」\
l_show suzu_ase
すずか「盗むんじゃなくて、これから捨てるも
    のを貰ってくるって言いたいんだね」\
r_show ari_n
アリサ「むしろ、ごみが減っていいじゃない。
    さあ、潜入の前に気合を入れて勉強を
    していきましょうか」\
csp SP_R : csp SP_L : print 1
bg "img/title#04.bmp", E_VFAST
wait 1000
delay 2000
bgm ms_setsume
bg "img/blackboard.bmp", E_FAST
speak_mode
r_show suzur_qes
すずか「Lispって式に括弧が多いよね。この括弧っていったい何なのかな?」\
r_show ari_n
アリサ「ふっふっふ……これこそがLispの面白いところ。プログラムである式と、/
データである値が等価であるという脅威の特徴を秘めるためのものなのよ」\
r_show suzur_T_T
すずか「アリサちゃん、むずかしすぎるよ…」\
r_show ari_n
アリサ「ふふっ。とにかく、Lispは面白いってこと!」\
r_show suzur_qes
すずか「値って#33FF334#FFFFFFとか#33FF33y#FFFFFFのことだよね。@/
式は#33FF33(ishida 30)#FFFFFFのように、括弧の中に値である数値やシンボルを含んだりもするけど、/
これも値なの?」\
r_show ari_n
アリサ「括弧で包まれた式は前に言った通り#FFFF33リスト#FFFFFFという構造なんだけど、/
これは#FFFF33コンス#FFFFFFという型の値を複数使ってできているの」\
r_show suzur_nc
すずか「うーん、よく分からないな。@/
とりあえずコンスっていうものについて教えて」\
r_show ari_n
アリサ「コンスとは、#FFFF33CAR部#FFFFFF、#FFFF33CDR部#FFFFFFという2つの記憶場所をもつ型なの。@/
CAR、CDRはそれぞれ、カー、クダーと発音するわ。@/
コンスはこれらを使って2つの値を覚えることが出来るのよ」\
r_show suzur_n
すずか「いまいちよく分からないな……実際に触った方が分かりやすいのかな」\
r_show ari_n
アリサ「コンスを作るにはconsという関数を使うの。@
#33FF33(cons ’a ’b)#FFFFFF
これを評価すると、#33FF33(a . b)#FFFFFFという値が返るわ。/
これは、シンボルaをCAR部に、シンボルbをCDR部に保持するコンスという意味よ。@/
show_dgm ":l;img/dgm0401.bmp"
ここから分かるように、関数consは第一引数をCAR部に、第二引数をCDR部に保持するコンスを作るわ」\
r_show suzur_exc
すずか「確かに、1つのコンスで2つの値を覚えてるね」\
csp SP_DGM0
r_show ari_n
アリサ「コンスに対してcarという関数を使うとCAR部の値が、cdrという関数を使うとCDR部の値が取得できるから、
#33FF33(car (cons ’a ’b))#FFFFFF⇒#33FF33a#FFFFFF
#33FF33(cdr (cons ’a ’b))#FFFFFF⇒#33FF33b#FFFFFF
という結果になるわ」\
r_show suzur_n
すずか「car、cdr、consは関数だから引数が先に評価されるんだね」\
r_show ari_n
アリサ「ここで注意しないといけないのが、コンスは2つの値を記憶しているわけではなく、/
2つの値の#FFFF33番地#FFFFFFを覚えているということよ」\
r_show suzur_qes
すずか「値の番地ってどういうこと?」\
r_show ari_n
アリサ「値というものは、作られると箱のようなものの中に記憶されると考えられるの。@/
その箱には一つ一つ固有の番号が付けられていて、この番号のことを#FFFF33番地#FFFFFFと呼ぶわ。@/
show_dgm ":l;img/dgm0402.bmp"
値は必ず箱の中のみに存在して、勝手に別の箱に移動したり、1つの箱の中に2つの値が入ったりするようなことはないの。@/
だから、値の番地さえ分かれば、値が分かっているのと同じことなのよ」\
r_show suzur_qes
すずか「それならコンスは、なんで値そのものじゃなくて、番地で記憶してるの?」\
r_show ari_n
アリサ「コンスのCARやCDRには、どんな値を覚えさせることもできるの。@/
つまり、型がコンスの値も入れることができるわ。@/
こればっかしは、値を直接覚える方法じゃ無理でしょ」\
r_show suzur_exc
すずか「ああ、なるほど」\
csp SP_DGM0
r_show ari_n
アリサ「コンスに対してconsを使う式
#33FF33(cons ’a (cons ’b ’c))#FFFFFF
これを評価した結果はどうなると思う?」\
r_show suzur_nc
すずか「えーと、まず、#33FF33(cons ’b ’c)#FFFFFFが評価されて、/
#33FF33(b . c)#FFFFFFというコンスになるよね。@/
それから、CAR部にシンボルa、CDR部にさっき作ったコンスが入ったコンスが作られるから、この式の値は/
#33FF33(a .(b . c))#FFFFFFかな?」\
r_show ari_n
アリサ「#33FF33(cons ’a (cons ’b ’c))#FFFFFFの値は#33FF33(a b . c)#FFFFFFと表示されるわ。@/
この表記に少々驚くかもしれないけど、この例のように、/
#FFFF33コンスのCDR部がコンス#FFFFFFの場合、ドットと括弧を省略するというのが決まりなの。@/
表示はドットと括弧が一つずつ消えてるけど、意味の上ではすずかが言ったので正解よ」\
r_show suzur_dotc
すずか「えーと、#33FF33(a b . c)#FFFFFFっていうのは、まず、コンスがあって、/
そのCARはシンボルa、CDRは別のコンス。/
で、そのコンスのCARがシンボルbでCDRはシンボルc@/
r_show suzur_ase
……なんだかややこしいね」\
r_show ari_n
アリサ「分かりにくいなら図を描いてみるといいわ。@/
show_dgm ":l;img/dgm0403.bmp"
図の描き方については補足説明の方で解説するわね」\
r_show suzur_nc
すずか「こんな感じでコンスを繋いでいったら確かに今まで入力してきた式みたいになりそうだけど、/
これだと最後に#33FF33(A B … Y . Z)#FFFFFFみたいにドット.が残っちゃうよね」\
r_show ari_n
アリサ「これも記法上のルールで、コンスのCDRが空リスト#33FF33()#FFFFFFという特殊な値の場合、/
CDR部の表示を省略するって決まりになってるの」\
r_show suzur_nc
すずか「確かに空リスト#33FF33()#FFFFFFっていうのは、中身がないリストっていう風に見えなくもないね。@/
これをコンスのCDR部に入れるといいの?」\
r_show ari_n
アリサ「具体的には、
#33FF33(cons ’a (cons ’b ’()))#FFFFFF
の値は#33FF33(a b)#FFFFFFになるわ。図を描くとこんな感じよ。@/
show_dgm ":l;img/dgm0404.bmp"
空リストは#33FF33’()#FFFFFFという風に、quoteして使うから注意してね」\
r_show suzur_exc
すずか「今まで式として書いてきたものはコンスというものが繋がってできたLispの値の集まりだったんだね」\
r_show ari_n
アリサ「最初、括弧に要素が入ったものをリストと呼んだけど、@/
正確には、空リストと、CDRにリストを格納したコンスのことをリストというの。@/
show_dgm ":l;img/dgm0405.bmp"
定義が再帰的になって分かりにくいかもしれないけど、意味はそのうちわかるわ」\
csp SP_DGM0
r_show suzur_n
すずか「けど、複雑なものを作ろうとしたら、consを一杯書かなくちゃいけないから大変だね」\
r_show ari_exc
アリサ「それが面白いことに、#33FF33’(a b c)#FFFFFFなんて式も書けちゃうのよ」\
r_show suzur_dotc
すずか「引用符’はquoteに変わるから#33FF33(quote (a b c))#FFFFFFに変形して、/
quoteの引数がそのまま結果になるから……@/
r_show suzur_exc
式の値は#33FF33(a b c)#FFFFFF……@式がそのままリストになっちゃった!」\
r_show ari_n
アリサ「どう? 式の書き方と値の表現方法が同じだから、式も値として扱えるの。@/
ちなみに#33FF33’(a . b)#FFFFFFみたいな式を書くことだって出来るのよ。@/
今まで式と呼んできたものは正確には#FFFF33S式#FFFFFFというの。@/
S式の詳細については用語集を読んでね」\
r_show suzur_n
すずか「頭が混乱しそうだし、そろそろ実際にやってみたいな」\
r_show ari_n
アリサ「ここで諸注意。『(1 2 3)というリストを作れ』といわれた時は、
#33FF33/
(cons 1
 (cons 2
  (cons 3 ’())))#FFFFFF
と書いてもいいけど、@/
#33FF33’(1 2 3)#FFFFFF
と書いた方が楽だからね。@/
あと、
#33FF33(cons x (cons y ’()))#FFFFFF
はxとyが評価されるけど、@/
#33FF33’(x y)#FFFFFF
はxとyはquoteの影響で評価されずに、シンボルのまま残るから注意してね」\
gosub *ex_init
*ex04
mov $problem_label, "*ex04_problem"
mov $tab_label, "*ex04_tab"
mov $ret_label, "*ex04"
mov $suc_label, "*ex04_check"
goto *ex_rep
*ex04_problem
!s0アリサ「まず、xの値を(a b c)というリストと定義しなさい」!sd
return
*ex04_check
gosub *pop : gosub *pop
;;(defien x '(a b c))の答え合わせ
mov $sarg0, "x"
gosub *create_symbol
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *find_var
mov %arg0, %ret
gosub *cdr
mov %arg0, %ret ;A
gosub *get_tag
mov %tmp1, %ret
mov $sarg0, "*ex04_miss1"
if %tmp1 != TAG_CONS goto *ex_check_fail
gosub *push ;S(A, new env)
mov $sarg0, "a"
gosub *create_symbol
mov %tmp, %ret ;a
gosub *pop ;A<S(new env)
mov %arg0, %ret
gosub *push ;S(A, new env)
mov %arg1, %arg0
mov %arg0, %tmp
gosub *push ;S(a, A, new env)
mov %arg0, %arg1
gosub *car
mov %tmp1, %ret ;CAR(A)
gosub *pop ;a<S(A, new env)
mov $sarg0, "*ex04_miss1"
if %tmp1 != %ret gosub *pop : goto *ex_check_fail
gosub *pop ;A<S(new env)
mov %arg0, %ret
gosub *cdr
mov %arg0, %ret ;B
gosub *get_tag
mov %tmp1, %ret
mov $sarg0, "*ex04_miss1"
if %tmp1 != TAG_CONS goto *ex_check_fail
gosub *push ;S(B, new env)
mov $sarg0, "b"
gosub *create_symbol
mov %tmp, %ret ;b
gosub *pop ;B<S(new env)
mov %arg0, %ret
gosub *push ;S(B, new env)
mov %arg1, %arg0
mov %arg0, %tmp
gosub *push ;S(b, B, new env)
mov %arg0, %arg1
gosub *car
mov %tmp1, %ret ;CAR(B)
gosub *pop ;b<S(B, new env)
mov $sarg0, "*ex04_miss1"
if %tmp1 != %ret gosub *pop : goto *ex_check_fail
gosub *pop ;B<S(new env)
mov %arg0, %ret
gosub *cdr
mov %arg0, %ret ;C
gosub *get_tag
mov %tmp1, %ret
mov $sarg0, "*ex04_miss1"
if %tmp1 != TAG_CONS goto *ex_check_fail
gosub *push ;S(C, new env)
mov $sarg0, "c"
gosub *create_symbol
mov %tmp, %ret ;c
gosub *pop ;C<S(new env)
mov %arg0, %ret
gosub *push ;S(C, new env)
mov %arg1, %arg0
mov %arg0, %tmp
gosub *push ;S(c, C, new env)
mov %arg0, %arg1
gosub *car
mov %tmp1, %ret ;CAR(C)
gosub *pop ;c<S(C, new env)
mov $sarg0, "*ex04_miss1"
if %tmp1 != %ret gosub *pop : goto *ex_check_fail
gosub *pop ;C<S(new env)
mov %arg0, %ret
gosub *cdr
mov $sarg0, "*ex04_miss1"
if %ret != %nil goto *ex_check_fail
;;(define x '(a b c))の答え合わせ終了
mov $sarg0, "x"
gosub *create_symbol
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *find_var
mov %arg0, %ret
gosub *cdr
mov %arg0, %ret ;A
gosub *push ;S(A, new env)
mov $sarg0, "*ex04_2"
goto *ex_check_pass ;S(A, new env)
*ex04_miss1
r_show ari_die
アリサ「……なに違うことやってるのよ。やり直し!」\
r_show ari_n
inc %adv_tmp
gosub *pop ;new env<S()
gosub *ex_init_env : goto *ex04
*ex04_2 ;S(A, new env)
mov $problem_label, "*ex04_2_problem"
mov $tab_label, "*ex04_2_tab"
mov $ret_label, "*ex04_2"
mov $suc_label, "*ex04_2_check"
goto *ex_rep
*ex04_2_problem
!s0アリサ「次に、xのCARを取り出しなさい」!sd
return
*ex04_2_check
gosub *pop
mov %arg0, %ret
gosub *pop
gosub *push ;S(new object, A, new env)
;;(car x)の答え合わせ
gosub *pop ;new object<S(A, new env)
mov %arg0, %ret
gosub *pop ;A<S(new env)
mov %tmp, %ret
gosub *push ;(new object, new env)
mov %arg0, %tmp
gosub *push ;S(A, new object, new env)
gosub *car
mov %tmp, %ret ;CAR(A)
gosub *pop ;A<S(new object, new env)
mov %tmp1, %ret
gosub *pop ;new object<S(new env)
mov %tmp2, %ret
mov $sarg0, "*ex04_3"
if %tmp2 == %tmp mov %arg0, %tmp1 : gosub *push : goto *ex_check_pass ;S(A, new env)
mov %arg0, %tmp1
gosub *push ;S(A, new env)
;ここでxの再定義をしてくれるという素晴らしい人のために
;(set-car!等は諦める方針で orz)
mov $sarg0, "x"
gosub *create_symbol
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *find_var
mov %arg0, %ret
gosub *cdr
mov %tmp, %ret
gosub *pop ;A<S(new env)
mov %arg0, %ret
mov $sarg0, "*ex04_ext"
if %tmp != %arg0 goto *ex_check_fail ;S(new env)
gosub *push ;S(A, new env)
mov $sarg0, "*ex04_2_normal_miss"
goto *ex_check_fail
*ex04_2_normal_miss
r_show ari_die
アリサ「違うわよ。もう一回やり直し!」\
r_show ari_n
inc %adv_tmp
goto *ex04_2
*ex04_ext
r_show ari_muka
アリサ「……なかなか面白いことをやってくれるわね。@/
r_show ari_do
最初からやり直し!!!」\
r_show ari_n
gosub *pop ;new env<S()
inc %adv_tmp
gosub *ex_init_env : goto *ex04
*ex04_3 ;S(A, new env)
mov $problem_label, "*ex04_3_problem"
mov $tab_label, "*ex04_3_tab"
mov $ret_label, "*ex04_3"
mov $suc_label, "*ex04_3_check"
goto *ex_rep
*ex04_3_problem
!s0アリサ「次に、xのCDRを取り出しなさい」!sd
return
*ex04_3_check
gosub *pop
mov %arg0, %ret
gosub *pop
gosub *push ;S(new object, A, new env)
;;(cdr x)の答え合わせ
gosub *pop ;new object<S(A, new env)
mov %arg0, %ret
gosub *pop ;A<S(new env)
mov %tmp, %ret
gosub *push ;(new object, new env)
mov %arg0, %tmp
gosub *push ;S(A, new object, new env)
gosub *cdr
mov %tmp, %ret ;CDR(A)
gosub *pop ;A<S(new object, new env)
mov %tmp1, %ret
gosub *pop ;new object<S(new env)
mov %tmp2, %ret
mov $sarg0, "*ex04_end"
if %tmp2 == %tmp goto *ex_check_pass_end ;S(new env)
mov %arg0, %tmp1
gosub *push ;S(A, new env)
;ここでxの再定義をしてくれるという素晴らしい人のために
;(set-car!等は諦める方針で orz)
mov $sarg0, "x"
gosub *create_symbol
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *find_var
mov %arg0, %ret
gosub *cdr
mov %tmp, %ret
gosub *pop ;A<S(new env)
mov %arg0, %ret
mov $sarg0, "*ex04_ext"
if %tmp != %arg0 goto *ex_check_fail ;S(new env)
gosub *push ;S(A, new env)
mov $sarg0, "*ex04_3_normal_miss"
goto *ex_check_fail
*ex04_3_normal_miss
r_show ari_die
アリサ「違うわよ。もう一回やり直し!」\
r_show ari_n
inc %adv_tmp
goto *ex04_3
*ex04_tab
csel "続ける", *ex04, "ヒント", *ex04_hint, "タイトルに戻る", *ex04_bye
*ex04_2_tab
csel "続ける", *ex04_2, "ヒント", *ex04_2_hint, "タイトルに戻る", *ex04_2_bye
*ex04_3_tab
csel "続ける", *ex04_3, "ヒント", *ex04_3_hint, "タイトルに戻る", *ex04_3_bye
*ex04_bye
mov %adv_rmode, 1 ;右クリック有効
textclear
csp SP_R
stop
goto *title
*ex04_2_bye
sub %sp, 2
goto *ex04_bye
*ex04_3_bye
sub %sp, 2
goto *ex04_bye
*ex04_hint
textclear
アリサ「変数の定義自体はもう大丈夫よね? 問題は#33FF33(a b c)#FFFFFFという値を作るところね。@/
#33FF33(a b c)#FFFFFFと直接書くと式として評価されて、関数呼び出しになっちゃうから駄目よ。@/
#33FF33’式#FFFFFFという風に、式をquoteすると、式の形のままのリストが得られて上手くいくわ」\
goto *ex04
*ex04_2_hint
textclear
アリサ「リストのCAR部を取得するには関数carを使うといいわ」\
goto *ex04_2
*ex04_3_hint
textclear
アリサ「リストのCDR部を取得するにはcar同様に、関数cdrを使うといいわ」\
goto *ex04_3
*ex04_end
mov %adv_rmode, 1 ;右クリック有効
gosub *pop ;new env<S()
saveon
mov %save_flag, 1 ;セーブ可能
textclear
add %adv_miss, %adv_tmp
bgm ms_setsume
if %adv_tmp == 0 goto *s_04_2
if %adv_tmp >=4 goto *s_04_3
*s_04_1
アリサ「まあまあだったわね」\
goto *s_04_4
*s_04_2
r_show ari_hrt
アリサ「全く問題ないようね」\
goto *s_04_4
*s_04_3
r_show ari_ase
アリサ「ちょっと失敗が多いわね」\
goto *s_04_4
*s_04_4
r_show suzur_n
すずか「#33FF33(car ’(a b c))#FFFFFFは#33FF33a#FFFFFFでシンボルになるのに、/
#33FF33(cdr ’(a b c))#FFFFFFは#33FF33(b c)#FFFFFFで、リストなんだね」\
r_show ari_n
アリサ「これはリストの構造を良く考えたら分かるわ。@/
show_dgm ":l;img/dgm0406a.bmp"
これが元のリストだけど、操作する対象はもちろん一番左のコンスなの。@/
show_dgm ":l;img/dgm0406b.bmp"
そう考えると、CARはシンボルでCDRがリストになる理由が分かるわ」\
r_show suzur_excc
すずか「あ、確かにリストになるね。リスト操作がいまいち分からない時は図を描くのがいいね」\
csp SP_DGM0 : print E_FAST
mov %adv_noroi_off, 1
vsp SP_R, 0
bg black, E_NORMAL
r_load ari_n
l_load suzu_n
bgm ms_jiku
bg "img/jiku_back.bmp", E_NORMAL
talk_mode
アリサ「なんとか時空管理局の施設の中に潜入
    できたわね」\
すずか「ほんと大変だったね」\
r_show ari_tun
アリサ「潜入するまでのエピソードは都合上、
    省略させていただきます」\
l_show suzu_qes
すずか「アリサちゃん、誰にいってるの?」\
r_show ari_n
アリサ「こっちにもいろいろあるのよ。鮫島、
    ちゃんとついてきてる?」\
l_show ":l;img/same.bmp"
鮫島「ここに」\
r_show ari_ase
アリサ「なのはに調べてもらったら、私にもす
    ずかにも魔法適性がなくて、鮫島にだ
    けあるとは思わなかったわ」\
鮫島「恐縮です」\
r_show ari_n
アリサ「まあいいわ、あんたが頼りだからね」\
鮫島「心得ております」\
vsp SP_R, 0 : vsp SP_L, 0
bg black, E_NORMAL
施設の中を探索すること数分\
lsp SP_TMP, ":l;img/dev.bmp", 100, 50
print E_SLOW
三人はデバイスを見つけ出した\
csp SP_TMP
print E_FAST
r_load ari_n
l_load suzu_hrt
bg "img/jiku_back.bmp", E_NORMAL
すずか「まさか、親切に取り扱い説明書まで一
    緒においてあるとは思わなかったね」\
アリサ「そのおかげで、すぐにでも使える状態
    になって、良かったじゃない」\
l_show suzu_nc
すずか「これで鮫島さんが魔法が使えるね」\
r_show ari_exc
アリサ「そうそう。魔法プログラムとして、こ
    れまでに、すずかが打ち込んだ式も入
    れといたからね」\
l_show suzu_n
すずか「!?」\
r_show ari_hrt
アリサ「#FFFF33間違えてなかったら#FFFFFF、ちゃんと強力な
    魔法が使えるはずだから大丈夫」\
l_show suzu_dot
すずか「つまり、間違えてたら、#FFFF33魔法の威力が
    落ちる#FFFFFFわけだね……」\
r_show ari_n
アリサ「さて、用は済んだし、すずか、鮫島、
    帰るわよ」\
l_show suzu_nc
すずか「そうだね」\
r_out
l_rout
delay 500
l_in ":l;img/jiku.bmp"
局員「誰だ! そこにいるのは!」\
r_show ari_ase
アリサ「あ……見つかった……」\
r_show suzu_ase
すずか「アリサちゃん、どうしよう!!」\
r_show ari_tun
アリサ「鮫島!!! あいつやっつけて!」\
r_show ":l;img/same.bmp"
鮫島「心得ております」\
stop
csp SP_R : csp SP_L
bg black, E_WIPE2
bgm ms_battle
gosub *battle04
if %ret == 0 goto *battle04_after
;;敗北
textclear
stop
goto *title
*battle04_after
l_load ":l;img/jiku.bmp"
bgm ms_jiku
bg "img/jiku_back.bmp", E_NORMAL
局員「く……ベルカ式の魔法か……」\
csp SP_L
print E_FAST
バタ\
r_show ari_hrt
l_show suzu_ase
すずか「アリサちゃん、この人、気絶しちゃっ
    たけど……」\
アリサ「今のうちに逃げましょう」\
すずか「局員を襲っちゃったわけだし、絶対に
    追っ手が来るよ!」\
r_show ari_tun
アリサ「うーん……しばらく何処かに身を潜め
    た方がいいかもしれないわね……」\
l_show suzu_exc
すずか「そうだ京都、行こう。」\
speak_mode
;;;;;第4話終了;;;;;
speak_mode
textclear
vsp SP_R, 0
vsp SP_L, 0
bg black, E_SLOW
;;共通記録の書き換え
csvopen "arisa.szk", "rc"
csvread %adv_clear
csvclose
if %adv_clear >= 4 goto *story04_after
mov %adv_clear, 4
csvopen "arisa.szk", "wc"
csvwrite %adv_clear
csvclose
#FFFF33補足説明#FFFFFFが追加されました。
#FFFF33用語集#FFFFFFに単語が追加されました。\
*story04_after
r_show suzur_nc
すずか「どうしようかな?」
csel "補足説明を読む", *story04_condicil, "用語集を読む", *story04_glossary, "タイトルに戻る", *title_back, "第5話へ進む", *story05
*story04_condicil
gosub *condicil
goto *story04_after
*story04_glossary
gosub *glossary
goto *story04_after
;;;;;第5話;;;;;
*story05
mov %adv_noroi_off, 1 ;;;λ山のためユーノは出さない
csp SP_R : csp SP_L : print E_FAST
stop
talk_mode
bgm ms_kyoto
bg "img/yama_b.bmp", E_FAST
r_in ari_n
l_in suzu_n
l_show suzu_hrt
すずか「というわけで舞台は京都に移ります」\
r_show ari_hrt
アリサ「後ろに見えるのが、有名な#FFFF33λ山#FFFFFFね……@/
r_show ari_ase
    というより、本当に京都に来たんだ」\
l_show suzu_nc
すずか「それはともかく、追っ手に備えないと
    いけないね」\
r_show ari_hrt
アリサ「とりあえず、前に倒したやつぐらいは
    簡単に倒せるくらいに魔法を強化しな
    いとね」\
l_show suzu_ase
すずか「全然解決に向かってない気が……」\
csp SP_R : csp SP_L : print 1
bg "img/title#05.bmp", E_VFAST
wait 1000
delay 2000
bgm ms_setsume
bg "img/blackboard.bmp", E_FAST
speak_mode
r_show suzur_n
すずか「けど、鮫島さんがどうにかして、泊まる場所と勉強のための場所を手配してくれて助かったね。@/
r_show suzur_dotc
……なんか、前の場所と似すぎている気もするけど」\
r_show ari_n
アリサ「それは置いておいて、今回は関数の説明をするわね」\
r_show suzur_nc
すずか「関数といったら、#33FF33car#FFFFFFとか#33FF33+#FFFFFFとか、/
評価された引数を受け取って、それに対応して値を返すものだよね」\
r_show ari_n
アリサ「今回は関数を使うんじゃなくて作ってみましょう。@/
手始めに、『引数に1を加えたものを返す関数』でも作ってみましょうか。
#33FF33(lambda (x) (+ x 1))#FFFFFF
lambda(ラムダ)というのは関数を作るためのスペシャルフォームよ。@/
引数は評価されないわ」\
r_show suzur_n
すずか「見た感じ、引数をつけてこの作られた関数を呼び出すと、/
#33FF33x#FFFFFFという名前でその引数を受け取って、@/
#33FF33(+ x 1)#FFFFFFの評価したものが結果となるのかな」\
r_show ari_n
アリサ「ここでのxのように、引数を受け取るシンボルを#FFFF33仮引数#FFFFFFというの。@/
今まで引数と言ってきた、関数に送る側の値は仮引数と区別する時には、#FFFF33実引数#FFFFFFというわ。@/
実引数はどんな型でもかまわないけど、仮引数は必ずシンボルだからね」\
r_show suzur_qes
すずか「仮引数の値は実引数なんだよね。@/
つまり、仮引数は実引数に束縛されるのかな?」\
r_show ari_n
アリサ「その話は後回しにしましょう。@/
関数自体は値の一種で、型が関数の値が表示される時は、/
#33FF33#<procedure>#FFFFFF/
となるわ。この表示から得られる情報は特にないわね」\
r_show suzur_nc
すずか「関数が値の一種ってことは、数や、シンボルと同等に扱えるんだね。@/
ただ、数やシンボルとは違って、表示してもあんまり意味はないんだ。@/
けど、なんかいまいちイメージが湧かないな……」\
r_show ari_n
アリサ「じゃあ、今度はさっきの関数を呼び出してみるわね。
#33FF33((lambda(x)(+ x 1))9)#FFFFFF
結果は#33FF3310#FFFFFFになるわ。@/
show_dgm ":l;img/dgm0501.bmp"
ピンとこないかもしれないけど、評価の規則をよく思い出してね」\
r_show suzur_n
すずか「式全体はリストだから#33FF33(関数 引数)#FFFFFFの関数呼び出しの形だよね。
関数は#33FF33(lambda(x)(+ x 1))#FFFFFF。引数は#33FF339#FFFFFFだね」\
r_show ari_n
アリサ「リストを評価する時は、演算子が被演算子に作用する前に、/
被演算子だけじゃなくて演算子も先に評価されるの。@/
show_dgm ":l;img/dgm0502.bmp"
だから、この場合、#33FF33(lambda (x) (+ x 1))#FFFFFFと#33FF339#FFFFFFが先に評価されるわ。@/
lambdaの式の結果はさっき言った通り関数になるわ。9は9のままね。@/
それから、この関数が呼び出されて、#33FF33(+ x 1)#FFFFFFの仮引数xを実引数9と置き換えた式の値、@/
すなわち#33FF33(+ 9 1)#FFFFFFの値である10が式全体の値となるわ」\
r_show suzur_exc
すずか「関数の中身に出てくる仮引数を、実引数と置き換えると考えたら分かりやすいね」\
csp SP_DGM0
r_show ari_n
アリサ「今度は引数を2個受け取る関数を書いてみるわね。/
言うまでもないけど、3個以上の時も同様にすれば出来るわ。
#33FF33/
(lambda (x y)
 (cons x (cons y ’())))#FFFFFF
複数行にわたって書いてるけど、実際に入力する時は、一行で書くのよ。/
本物のLispは、もちろん複数行に渡って書いていいんだけど、/
NScLisperはNScripterの仕様上、一行しか書けないの」\
r_show suzur_nc
すずか「えーと、呼び出す時は
#33FF33/
((lambda (x y)
  (cons x
        (cons y ’())))
 ’a ’b)#FFFFFF
だね。#33FF33(cons x (cons y ’()))#FFFFFFのxとyを/
#33FF33’a#FFFFFFと#33FF33’b#FFFFFFを評価した値、すなわち#33FF33a#FFFFFFと#33FF33b#FFFFFFに置き換えるから、/
結果は#33FF33(a b)#FFFFFFかな?」\
r_show ari_n
アリサ「ただ、これじゃあ、関数呼び出しのたび長い式を書かないといけないから、関数を束縛しましょう。
#33FF33/
(define caar
 (lambda(lst)
  (car(car lst))))#FFFFFF
関数は値の一種だから、これまでと同様にdefineが使えて、@/
これで#33FF33caar#FFFFFFが関数に束縛されるわ。@/
こうやって、関数を束縛することを『関数の定義』というの。@/
この例の場合、『関数caarを定義する』というわね。@/
あと、#FFFF33関数caar#FFFFFFという言い方をする場合、caarと結びついた関数を指すから注意してね」\
r_show suzur_qes
すずか「ただ『関数』とだけいってたら分かりにくいから、付けた名前で呼ぶこともあるんだね。@/
関数は一度束縛してしまえば、作り直さなくてもいいの?」\
r_show ari_n
アリサ「束縛したら、lambdaの式を書く代わりに、束縛されたシンボルを使って、
#33FF33(caar ’((a b)(c d)))#FFFFFF
とすればいいの。この式の結果はaになるわね」\
r_show suzur_nc
すずか「えーと、caarはシンボルだから、評価すると、対応する値に変わるんだね。@/
show_dgm ":l;img/dgm0503.bmp"
で、対応しているのは関数だから上手くいくんだ。@/
r_show suzur_qes
あれ? じゃあ、同じように+とかcarも、実は関数と結びついたシンボルだったの?」\
csp SP_DGM0
r_show ari_exc
アリサ「そういうこと。いままで+やcarを関数と呼んできたけど、これは正確には/
#FFFF33関数と結びついたシンボル#FFFFFFなの。@/
今までの『関数car』みたいな言い方は、carと結びついた関数のことを指してたのよ」\
r_show suzur_excc
すずか「+やcarは、シンボル自身に特別な意味があるんじゃなくて、単に関数に束縛されてただけなんだ」\
r_show ari_n
アリサ「じゃあ、改めて、リストの評価の規則を説明するわね。
#33FF33(関数 引数1 引数2…引数n)#FFFFFF
この場合は、引数1~nと関数を評価する。そして関数を評価して得られた値に評価済みの引数1~nを渡す。@/
ここで言う『関数』は、値としての関数じゃなくて、関数に束縛されたシンボルやlambdaの式のように/
『評価したら関数になる式』と言う意味だから間違えないでね」\
r_show suzur_nc
すずか「少し分かったような分からないような……。とりあえず、話をもどすと、関数を作るには
#33FF33(lambda (仮引数) 式)#FFFFFF
とするんだね。引数は何個でも自由に取れて、式の部分は自由に書けるんだね」\
r_show ari_n
アリサ「使う時は、一度束縛すると便利だけど、別に束縛しなくても使えるからね」\
r_show suzur_n
すずか「じゃあ、そろそろ試してみたいな」\
r_show ari_n
アリサ「練習問題の前に少しリストの復習。@/
リストはquoteを使って#33FF33’(a b c)#FFFFFFという風に作れるわ。@/
このa、b、cのことをそれぞれ、第一要素、第二要素、第三要素と呼びましょう。@/
これのCARを取ると#33FF33a#FFFFFF、CDRを取ると#33FF33(b c)#FFFFFFとなるわ。@/
よって、carはリストの第一要素を取り出す関数、@/
cdrはリストの第一要素を抜き取ったリストを返す関数/
と考えることもできるわ」\
gosub *ex_init
*ex05
mov $problem_label, "*ex05_problem"
mov $tab_label, "*ex05_tab"
mov $ret_label, "*ex05"
mov $suc_label, "*ex05_check"
goto *ex_rep
*ex05_problem
!s0アリサ「関数fを『引数の第二要素を返す関数』として定義しなさい。/
ただし、引数は1つで、リストのみが来ると考えていいわ」!sd
return
*ex05_check
gosub *pop : gosub *pop
;;;検証1
mov $sarg0, "(f '(k l))"
gosub *input_to_lobject
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *eval_form
mov %arg0, %ret
gosub *push ;S(l?)
mov $sarg0, "l"
gosub *create_symbol
mov %arg0, %ret
gosub *pop ;l?<S()
mov $sarg0, "*ex05_miss"
if %arg0 != %ret goto *ex_check_fail
;;;検証2
mov $sarg0, "(f '(x miko))"
gosub *input_to_lobject
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *eval_form
mov %arg0, %ret
gosub *push ;S(miko?)
mov $sarg0, "miko"
gosub *create_symbol
mov %arg0, %ret
gosub *pop ;miko?<S()
mov $sarg0, "*ex05_2_prev"
if %arg0 == %ret goto *ex_check_pass
mov $sarg0, "*ex05_miss"
goto *ex_check_fail
*ex05_miss
textclear
r_show ari_die
アリサ「違うわ。やり直し!」\
r_show ari_n
inc %adv_tmp
goto *ex05
*ex05_2_prev
mov $sarg0, "f"
gosub *create_symbol
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *find_var
mov %arg0, %ret
gosub *cdr
mov %arg0, %ret
gosub *push ;S(f)
*ex05_2
mov $problem_label, "*ex05_2_problem"
mov $tab_label, "*ex05_2_tab"
mov $ret_label, "*ex05_2"
mov $suc_label, "*ex05_2_check"
goto *ex_rep
*ex05_2_problem
!s0アリサ「引数#33FF33’(a b c)#FFFFFFをつけてfを呼び出しなさい」!sd
return
*ex05_2_check
gosub *pop
mov %arg0, %ret
gosub *pop
gosub *push
mov $sarg0, "b"
gosub *create_symbol
mov %arg0, %ret
gosub *pop ;ret<S(f)
mov $sarg0, "*ex05_end"
if %arg0 == %ret gosub *pop : goto *ex_check_pass_end
;;;fの再定義をcheck;;;
mov $sarg0, "f"
gosub *create_symbol
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *find_var
mov %arg0, %ret
gosub *cdr
mov %arg0, %ret
gosub *pop ;f<S()
mov $sarg0, "*ex05_2_miss"
if %arg0 != %ret goto *ex_check_fail
mov %arg0, %ret
gosub *push ;S(f)
mov $sarg0, "*ex05_2_normal_miss"
goto *ex_check_fail
;;;end;;;
*ex05_2_normal_miss
textclear
r_show ari_die
アリサ「違うわ。やり直し!」\
r_show ari_n
inc %adv_tmp
goto *ex05_2
*ex05_2_miss
textclear
r_show ari_muka
アリサ「……なかなか面白いことをやってくれるわね。@/
r_show ari_do
最初からやり直し!!!」\
r_show ari_n
inc %adv_tmp
goto *ex05
*ex05_tab
csel "続ける", *ex05, "ヒント", *ex05_hint, "タイトルに戻る", *ex05_bye
*ex05_2_tab
csel "続ける", *ex05_2, "ヒント", *ex05_2_hint, "タイトルに戻る", *ex05_2_bye
*ex05_bye
mov %adv_rmode, 1 ;右クリック有効
textclear
csp SP_R
stop
gosub *pop ;new env<S()
goto *title
*ex05_2_bye
gosub *pop ;f<S(new env)
goto *ex05_bye
*ex05_hint
textclear
アリサ「関数fを定義するんだから、式全体は#33FF33(define f 関数)#FFFFFFだわ。@/
受け取る引数は一つだから#33FF33関数#FFFFFFの部分には#33FF33(lambda (x) 式)#FFFFFFと書くといいわ。@/
#33FF33式#FFFFFFには『x』の第二要素を取り出す式を書くの。@/
これは、問題をやる直前に私が言ったことが大ヒントね。@/
もう一度いうと、carはリストの第一要素を取り出す関数で、cdrはリストの第一要素を抜き取ったリストを返す関数。@/
#33FF33(cdr x)#FFFFFFの結果はリストで、その第一要素はxの第二要素なんだから……」\
goto *ex05
*ex05_2_hint
textclear
アリサ「単純に#33FF33(関数 引数)#FFFFFFの式を書けばいいわ」\
goto *ex05_2
*ex05_end
mov %adv_rmode, 1 ;右クリック有効
gosub *pop ;new env<S()
saveon
mov %save_flag, 1 ;セーブ可能
textclear
add %adv_miss, %adv_tmp
bgm ms_setsume
if %adv_tmp == 0 goto *s_05_2
if %adv_tmp >=4 goto *s_05_3
*s_05_1
アリサ「まあまあだったわね」\
goto *s_05_4
*s_05_2
r_show ari_hrt
アリサ「全く問題ないようね」\
goto *s_05_4
*s_05_3
r_show ari_ase
アリサ「ちょっと失敗が多いわね」\
goto *s_05_4
*s_05_4
r_show suzur_nc
すずか「『仮引数は実引数で置き換えられる』と考えてきたけど、本当に置き換えられるわけじゃないよね。@例えば
#33FF33/
(define q
 (lambda (x)
  ’x))#FFFFFF
本当に、仮引数を実引数で置き換えたら、#33FF33(q 3)#FFFFFFの値は’3を評価した結果#33FF333#FFFFFFになっちゃうけど、/
これはちゃんと#33FF33x#FFFFFFになるでしょ」\
r_show ari_n
アリサ「すずかが最初に言った通り、本当は#FFFF33仮引数は実引数に束縛される#FFFFFFわ。@/
ただ、それだけだと問題があるの。@
#33FF33/
(define x 1)⇒x
(define y 3)⇒y
(define f
 (lambda (x)
  (+ x y))) ⇒f
(f 7)       ⇒10#FFFFFF
この4つの式を順番に評価していったら環境はどうなると思う?」\
r_show suzur_dotc
すずか「えーと、最初にxが1に束縛される。@それからyが3に束縛される。@/
そして、fが関数で束縛される。@最後にfに7をつけて呼び出す。@/
そうすると、仮引数のxが実引数の7に束縛されて……@/
r_show suzur_exc
あれ? xが2回束縛されちゃう!」\
r_show ari_n
アリサ「そうなるとややこしいから、『fの仮引数のx』と『関数の外側のx』を別物として扱いたいわね。@/
そこで、関数を呼び出すと新たな空っぽの環境が作られて、そこで仮引数が実引数に束縛されて、@/
その環境の元で、関数の中身の式を評価したらどうかしら?」\
r_show suzur_nc
すずか「環境が二つできるわけだね。ちょっとややこしいかな」\
r_show ari_n
アリサ「それじゃあ、ここでは新たに作る環境を#FFFF33新しい環境#FFFFFF、それまで使っていた環境を#FFFF33古い環境#FFFFFF/
とでも呼ぶことにするわね。@/
show_dgm ":l;img/dgm0504.bmp"
すると、関数呼び出しの間だけ、新しい環境が使われて、関数呼び出しが終わったら古い環境が再び使われることになるわ」\
r_show suzur_dotc
すずか「えーと、つまり#33FF33(f 7)#FFFFFFを評価すると、『xは7』という環境が作られて、@/
その元で、#33FF33(+ x y)#FFFFFFが評価されるんだよね。@/
r_show suzur_T_T
けど、そうなると、xは環境から7だと分かるけど、yは環境にないから、この式は評価できないよ」\
csp SP_DGM0
r_show ari_n
アリサ「そう。だからもうひと頑張り必要ね。@/
話は少し変わるけど、仮引数のことを#FFFF33束縛変数#FFFFFFともいうの。この場合xのことね。/
束縛変数の束縛は新しい環境に必ず含まれているわ。@/
それに対して、ここでのyのように、関数の仮引数でないけど、関数の中身の式に登場する変数を、/
#FFFF33自由変数#FFFFFFというわ」\
r_show suzur_n
すずか「仮引数を束縛変数、それ以外の変数を自由変数というわけだね」\
r_show ari_n
アリサ「自由変数の束縛は新しい環境の中には入っていないけど、/
関数の外側――つまり、古い環境に入ってるはずよね。@/
そこで、lambdaは関数を作る時に、評価に使った環境を/
これから作る関数に記憶させるの。@/
show_dgm ":l;img/dgm0505.bmp"
この場合、『xは1』と『yは3』という束縛のある環境ね」\
r_show suzur_n
すずか「つまり、関数は中身である式以外にも、lambdaが評価された環境を保持するんだ」\
r_show ari_n
アリサ「ただ、保持するといっても、コンスと同じく、直接環境のデータ全てを覚えるんじゃなくて、/
環境というものがある位置を覚えるの。『保持するというより』は、『矢印を向ける』という方がイメージしやすいかもしれないわね」\
r_show suzur_n
すずか「環境を丸々コピーすしたりするわけじゃないから、速度とかを心配することはないんだね」\
r_show ari_n
アリサ「作った関数を呼び出す時には、新しい環境を作って、仮引数を実引数で束縛した後、/
その環境の後ろに#FFFF33関数が保持している環境#FFFFFFを繋げるの。@/
show_dgm ":l;img/dgm0506.bmp"
変数を評価する時、まず新しい環境の中から探して、なければ繋げた環境の方を探すの」\
r_show suzur_nc
すずか「そうなると、#33FF33(+ x y)#FFFFFFを評価する時は、@/
xを新しい環境から探して値を見つける。@/
yを新しい環境から探すけど、見つからない。@/
だから、繋いである環境から探して、値を見つける。@/
こうやって、正しい結果を得るんだ」\
csp SP_DGM0
r_show ari_n
アリサ「これは、インタプリタの実装の一例で、全てのインタプリタがこう作られてるわけではないわ。@/
けど、つくりが違っても、結果は同じだから安心していいわよ」\
r_show suzur_ase
すずか「なんか分かったような分からないような話だね」\
r_show ari_n
アリサ「まあ、これはすぐには分からなくていいわ。@/
ただ、関数の仮引数と、関数の外側の変数の名前が一致した場合、仮引数の方が使われるということは覚えておいてね」\
mov %adv_noroi_off, 0 ;;ユーノ叩き再開
vsp SP_R, 0
bg black, E_NORMAL
r_load ari_exc
l_load suzu_n
bgm ms_after
bg "img/blackboard.bmp", E_NORMAL
talk_mode
すずか「この建物の窓からはλ山じゃなくて、
    『妙』と『法』が見えるのね」\
l_show suzu_qes
すずか「そういえば、局員の人が#FFFF33ベルカ式#FFFFFFの魔
    法っていってたけど、一体なんのこと
    だったのかな?」\
r_show ari_n
アリサ「調べてみたんだけど、魔法にはミッド
    式と、ベルカ式という二つの流派があ
    るらしいの」\
すずか「なるほど。けど、どうして私たちの魔
    法はベルカ式だったんだろう?」\
アリサ「ミッド式はCommonLispで、
    ベルカ式はSchemeらしいわ」\
l_show suzu_dot
すずか「……凄い設定だね」\
r_show ":l;img/arin.bmp"
アリサ「ありーん」\
アリサ「設定とか言わないように!」\
;;;;;第5話終了;;;;;
speak_mode
textclear
vsp SP_R, 0
vsp SP_L, 0
bg black, E_SLOW
;;共通記録の書き換え
csvopen "arisa.szk", "rc"
csvread %adv_clear
csvclose
if %adv_clear >= 5 goto *story05_after
mov %adv_clear, 5
csvopen "arisa.szk", "wc"
csvwrite %adv_clear
csvclose
#FFFF33補足説明#FFFFFFが追加されました。
#FFFF33用語集#FFFFFFに単語が追加されました。\
*story05_after
r_show suzur_nc
すずか「どうしようかな?」
csel "補足説明を読む", *story05_condicil, "用語集を読む", *story05_glossary, "タイトルに戻る", *title_back, "第6話へ進む", *story06
*story05_condicil
gosub *condicil
goto *story05_after
*story05_glossary
gosub *glossary
goto *story05_after
;;;;;第6話;;;;;
*story06
mov %adv_noroi_off, 0
csp SP_R : csp SP_L : print E_FAST
stop
talk_mode
bgm ms_kyoto
bg "img/blackboard.bmp", E_FAST
r_in ari_n
l_in suzu_n
すずか「鮫島さんってさ」\
r_show ari_qes
アリサ「ん、何?」\
すずか「57歳だよね」\
アリサ「たしか、そうだったと思うけど、それ
    がどうかしたの?」\
l_show suzu_dot
すずか「いや、私たちが魔法使うんなら『魔法
    少女』って感じなんだけどさ、鮫島さ
    んの場合……」\
r_show ari_dere
アリサ「魔法!w250…!w250…!w500老人?」\
l_show suzu_dere
すずか「老人はちょっと酷いと思うな」\
r_show ari_do
アリサ「うるさいうるさいうるさい! もう、
    魔導師でいいじゃない!」\
csp SP_R : csp SP_L : print 1
bg "img/title#06.bmp", E_VFAST
wait 1000
delay 2000
bg "img/blackboard.bmp", E_FAST
bgm ms_setsume
speak_mode
r_show ari_n
アリサ「今回は例から入っていきましょう。@
#33FF33/
(if (= x 0)
    1
    (- x 1))#FFFFFF
この式は評価されると、xの値が0の場合、1になり、それ以外の場合、xから1引いた数になるわ」\
r_show suzur_nc
すずか「#33FF33if#FFFFFFと#33FF33=#FFFFFFっていうのがはじめて出てきたね。@/
=は第一引数と第二引数が同じ数かどうか確かめている感じだよね。@/
で、ifはその結果を見て第二引数か第三引数かどちらかを返すという感じかな」\
r_show ari_n
アリサ「=は引数として二つの数を受け取る関数で、二つの数が等しい場合、#33FF33#t#FFFFFFという値、異なる場合は#33FF33#f#FFFFFFという値を返すわ。@/
#tと#fはSchemeで正しい(#FFFF33真#FFFFFF)・正しくない(#FFFF33偽#FFFFFF)を表すための値よ。/
型は#FFFF33論理型#FFFFFFといって、この型の値は#tと#fしかないのよ。@/
一方、ifは三つの式を受け取るスペシャルフォームなの。@/
第一引数を評価して、その値が真の場合、第二引数を評価して、それが結果となるの。@/
もし、第一引数を評価して、その値が偽の場合、第三引数を評価して、それが結果となるわ。@/
真・偽と言うのはさっき言った#tと#fのことだけど、#f以外の値は全て真という扱いだから注意してね。@/
つまり、偽といったら、#fのことだけど、真といったら、#f以外の値全てのことなの」\
r_show suzur_excc
すずか「ifは複雑な動作をするね。@/
第一引数を評価した値が真の場合は、第三引数は評価#FFFF33されない#FFFFFFし、@/
第一引数を評価した値が偽の場合は、第二引数は評価#FFFF33されない#FFFFFFんだね」\
r_show ari_n
アリサ「似たようなスペシャルフォームとして、#33FF33cond#FFFFFFというものがあるわ。@
#33FF33/
(cond (条件式1 実行式1)
      (条件式2 実行式2)
      ・・・
      (条件式n 実行式n)
      (else 実行式e))#FFFFFF
この式を評価すると、まず、条件式1を評価して、それが真の場合、実行式1を評価してその値を返す。@/
偽だったら、条件式2を評価して、それが真の場合、実行式2を評価してその値を返す。@/
同様に条件式nまで繰り返して、それも偽だった場合は、実行式eを評価してその値を返すの。@/
条件式は何個書いてもいいわ」\
r_show suzur_nc
すずか「condは条件式を順番に評価して、真になったところに対応する実行式を評価してその値を返すんだね。@/
全ての条件式が偽だった場合は、elseに続く実行式eを評価してその値を返すんだ。@/
これを使ってさっきの式を書き換えると
#33FF33/
(cond ((= x 0) 1)
      (else (- x 1)))#FFFFFF
と書けるね」\
r_show ari_n
アリサ「condもif同様に、評価しない引数が出てくるわね。@/
これまでの例だけを見てると、#33FF331#FFFFFFと#33FF33(- x 1)#FFFFFFを両方評価しても問題はないけど、/
両方評価されたら困る場合というのがあるの。@
#33FF33/
(define fact
 (lambda (n)
  (if (= n 0)
      1
      (* n
         (fact (- n 1
))))))#FFFFFF
読みにくくて悪いけど、これは階乗を求める関数を定義してるの。@/
nの階乗とは『n×(n-1)×(n-2)×…×1』のことよ。@/
#33FF33(fact 3)#FFFFFFを評価すると、3×2×1が計算されて6となるわ」\
r_show suzur_dotc
すずか「最初、実引数が0でないとすれば、/
#33FF33n#FFFFFFに#33FF33(fact (- n 1))#FFFFFFを掛けたものが結果になるんだね。@/
factがfactの中で呼ばれるけど、実引数は小さくなっていくから、いつか0になる。@/
すると、factは呼ばれずに、1が返されるから……確かに階乗の計算になってるね」\
r_show ari_n
アリサ「ここで、『ifは評価しない引数をもつ』という特徴が使われているわ。@/
もし、第二引数と第三引数両方を常に評価するとすれば、nが0の時もfactが呼ばれて、/
永遠にfactの呼び出しが終わることはないわ」\
r_show suzur_n
すずか「関数が自分自身を呼び出しているのが面白いよね。@/
lambdaの式を評価している時点では、まだfactの値は定義されてないけど、/
上手くいくってのがちょっと不思議だな……」\
r_show ari_n
アリサ「関数が、自分自身を呼ぶことを#33FF33再帰呼び出し#FFFFFFというの。@/
これを略して再帰ということもあるわ。@/
factを定義する前に、factを呼び出す式をlambdaの中で書いてるけど、/
これが上手くいく理由を説明するには、また環境の話をすることになるの。@/
前に言った通り、lambdaが呼ばれて作られた関数は、その時の環境を保持するわ。@/
その後にdefineでfactと関数の束縛が環境に加えられるわけだけど、@/
関数は環境に矢印を向けているだけだから、ちゃんと新たな束縛が反映されるの。/
だから、上手くいくのよ」\
r_show suzur_ase
すずか「かなり頭が混乱してるけど、一応何か練習しておきたいな」\
r_show ari_n
アリサ「再帰呼び出しを使った式は最悪、評価がずっと終わらないという危険性もあるわ。@/
NScLisperでは、あまりに評価に時間がかかると、無理やり評価を終えるか聞いてくるようになってるの。@/
ただ、無理やり終えるとプログラム自体が終了してしまうわ。@/
だから、ここでいったんセーブしておくことをお勧めするわ。@/
あと、回答に自信がなかったらTabキーを押してヒントを見た方がいいわね」\
gosub *ex_init
*ex06
mov $problem_label, "*ex06_problem"
mov $tab_label, "*ex06_tab"
mov $ret_label, "*ex06"
mov $suc_label, "*ex06_check"
goto *ex_rep
*ex06_problem
!s0アリサ「関数sを『引数としてnを受け取ると、1+2+…+nを返す関数』として定義しなさい。/
ただし、引数は1つで、数のみが来ると考えていいわ」!sd
return
*ex06_check
gosub *pop : gosub *pop
;;;検証1
mov $sarg0, "(s 0)"
gosub *input_to_lobject
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *eval_form
mov %arg0, %ret
gosub *get_data
mov $sarg0, "*ex06_miss"
if %ret != 0 goto *ex_check_fail
;;;検証2
mov $sarg0, "(s 6)"
gosub *input_to_lobject
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *eval_form
mov %arg0, %ret
gosub *get_data
mov $sarg0, "*ex06_2_prev"
if %ret == 21 goto *ex_check_pass
mov $sarg0, "*ex06_miss"
goto *ex_check_fail
*ex06_miss
textclear
r_show ari_die
アリサ「違うわ。やり直し!」\
r_show ari_n
inc %adv_tmp
goto *ex06
*ex06_2_prev
mov $sarg0, "s"
gosub *create_symbol
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *find_var
mov %arg0, %ret
gosub *cdr
mov %arg0, %ret
gosub *push ;S(s)
*ex06_2
mov $problem_label, "*ex06_2_problem"
mov $tab_label, "*ex06_2_tab"
mov $ret_label, "*ex06_2"
mov $suc_label, "*ex06_2_check"
goto *ex_rep
*ex06_2_problem
!s0アリサ「引数#33FF3310#FFFFFFをつけてsを呼び出しなさい」!sd
return
*ex06_2_check
gosub *pop ;ret<S(input, s)
mov %arg0, %ret
gosub *pop ;input<S(s)
gosub *get_data
mov $sarg0, "*ex06_end"
if %ret == 55 gosub *pop : goto *ex_check_pass_end
;;;sの再定義をcheck;;;
mov $sarg0, "s"
gosub *create_symbol
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *find_var
mov %arg0, %ret
gosub *cdr
mov %arg0, %ret
gosub *pop ;s<S()
mov $sarg0, "*ex06_2_miss"
if %arg0 != %ret goto *ex_check_fail
mov %arg0, %ret
gosub *push ;S(s)
mov $sarg0, "*ex06_2_normal_miss"
goto *ex_check_fail
;;;end;;;
*ex06_2_normal_miss
textclear
r_show ari_die
アリサ「違うわ。やり直し!」\
r_show ari_n
inc %adv_tmp
goto *ex06_2
*ex06_2_miss
textclear
r_show ari_muka
アリサ「……なかなか面白いことをやってくれるわね。@/
r_show ari_do
最初からやり直し!!!」\
r_show ari_n
inc %adv_tmp
goto *ex06
*ex06_tab
csel "続ける", *ex06, "ヒント", *ex06_hint, "タイトルに戻る", *ex06_bye
*ex06_2_tab
csel "続ける", *ex06_2, "ヒント", *ex06_2_hint, "タイトルに戻る", *ex06_2_bye
*ex06_bye
mov %adv_rmode, 1 ;右クリック有効
textclear
csp SP_R
stop
gosub *pop ;new env<S()
goto *title
*ex06_2_bye
gosub *pop ;s<S()
goto *ex06_bye
*ex06_hint
textclear
アリサ「受け取る引数は一つだから#33FF33(lambda (n) 式)#FFFFFFと書くといいわ。@/
けど、難しいのはこの式の部分ね。@/
1+2+…+(n-1)+nの計算をするんだけど、これは階乗の計算とよく似てるわね。@/
nが0の時は当然0になるわ。@/
それ以外の時には、nに1+2+…(n-1)を足したものになるわ。@/
sは1+2+…nを計算する関数なんだから、引数としてn-1を送ると、1+2+…(n-1)が計算できそうね。@/
つまり、sの中でsを呼び出す再帰呼び出しを使えばいいの。@/
すると、sの定義は、nが0なら#33FF330#FFFFFF、@それ以外なら、#33FF33(+ n (s (- n 1)))#FFFFFFと書けるわね。@/
あとは、ifかcondを使ってこれを式にするだけよ」\
goto *ex06
*ex06_2_hint
textclear
アリサ「単純に#33FF33(関数 引数)#FFFFFFの式を書けばいいわ」\
goto *ex06_2
*ex06_end
mov %adv_rmode, 1 ;右クリック有効
gosub *pop ;new env<S()
saveon
mov %save_flag, 1 ;セーブ可能
textclear
add %adv_miss, %adv_tmp
bgm ms_setsume
if %adv_tmp == 0 goto *s_06_2
if %adv_tmp >=4 goto *s_06_3
*s_06_1
アリサ「まあまあだったわね」\
goto *s_06_4
*s_06_2
r_show ari_hrt
アリサ「全く問題ないようね」\
goto *s_06_4
*s_06_3
r_show ari_ase
アリサ「ちょっと失敗が多いわね」\
goto *s_06_4
*s_06_4
r_show suzur_n
すずか「それにしても、練習問題でも入力する量が大分多くなってきたね」\
r_show ari_n
アリサ「実は、関数の定義をちょっとだけ短く書く方法が用意されていたりするの。
#33FF33/
(define (関数名 仮引数1…仮引数n)
 式)
(define 関数名
 (lambda (仮引数1…仮引数n)
  式))#FFFFFF
この二つの式は全く同じものとして扱われるの」\
r_show suzur_hrt
すずか「lambdaと括弧一つ分を書く手間が省けるね。@/
こんな便利なものがあるなら、もっと早く言ってくれてもよかったのに」\
r_show ari_n
アリサ「確かに便利といえば便利なんだけど、仕組みを分かりにくくしてしまうの。@/
defineはあくまでも、束縛を作るだけのスペシャルフォーム。/
関数を作るための特殊な機構を持っているわけじゃないの。@/
この新しい書き方は、あくまでも今までの書き方を#FFFF33省略#FFFFFFした書き方だからね」\
r_show suzur_n
すずか「逆に言えば、それさえ理解したら、好きなだけ使って問題ないわけだね」\
r_show ari_tun
アリサ「まあ、そうなんだけど。@このほかにquoteに対する引用符’のように、同じことをするのに、/
用意されている短い書き方を#FFFF33糖衣構文#FFFFFF(syntax sugar)というわ」\
r_show suzur_nc
すずか「便利だけど、使う時は、どういう意味のものなのかしっかり理解して使いましょうってことかな」\
vsp SP_R, 0
bg black, E_NORMAL
r_load ari_n
l_load suzu_n
bgm ms_after
bg "img/blackboard.bmp", E_NORMAL
talk_mode
すずか「ずっと追われる立場のままじゃいけな
    いと思うけど、どうしようか?」\
アリサ「機会を見て、なのはとコンタクトを取
    ってみましょう」\
l_show suzu_qes
すずか「それで上手くいくかな?」\
r_show ari_hrt
アリサ「なのはって、時空管理局の偉い人と知
    り合いらしいの。頼んだらきっと上手
    くうやむやにしてくれるわ」\
l_show suzu_ase
すずか「うわぁ……」\
r_show ari_n
アリサ「まあ、すぐに頼んだらさすがに迷惑だ
    から、ほとぼりが冷めるまでしばらく
    はここで大人しくしてましょう」\
;;;;;第6話終了;;;;;
speak_mode
textclear
vsp SP_R, 0
vsp SP_L, 0
bg black, E_SLOW
;;共通記録の書き換え
csvopen "arisa.szk", "rc"
csvread %adv_clear
csvclose
if %adv_clear >= 6 goto *story06_after
mov %adv_clear, 6
csvopen "arisa.szk", "wc"
csvwrite %adv_clear
csvclose
#FFFF33補足説明#FFFFFFが追加されました。
#FFFF33用語集#FFFFFFに単語が追加されました。\
*story06_after
r_show suzur_nc
すずか「どうしようかな?」
csel "補足説明を読む", *story06_condicil, "用語集を読む", *story06_glossary, "タイトルに戻る", *title_back, "第7話へ進む", *story07
*story06_condicil
gosub *condicil
goto *story06_after
*story06_glossary
gosub *glossary
goto *story06_after
;;;;;第7話;;;;;
*story07
mov %adv_noroi_off, 0
csp SP_R : csp SP_L : print E_FAST
stop
talk_mode
bgm ms_kyoto
bg "img/blackboard.bmp", E_FAST
r_in ari_n
l_in suzu_n
l_show suzu_qes
すずか「なのはちゃんの魔法は遠距離から攻撃
    したりすることができるらしいけど、
    そういうのはできないかな?」\
r_show ari_tun
アリサ「うーん、そういう魔法プログラムを入
    れたところで、術者が使えないと意味
    がないのよ」\
l_show ":l;img/same.bmp"
鮫島「恥ずかしながら、どうやら私には使えな
   いようです」\
アリサ「だそうよ」\
l_show suzu_dotc
すずか「うーん、魔法の有効範囲が狭いってい
    うのは、ちょっと残念だね」\
csp SP_R : csp SP_L : print 1
bg "img/title#07.bmp", E_VFAST
wait 1000
delay 2000
bgm ms_setsume
bg "img/blackboard.bmp", E_FAST
speak_mode
r_show ari_n
アリサ「変数の話をするわね。
#33FF33/
(define x 1)
(define y 2)
(define f
 (lambda (n)
  (+ n x)))#FFFFFF
このx、y、fのように定義した変数はどこからでも使えるの。@/
例えば、関数を作って、その中からでも使えるでしょ。@/
こういった変数のことを#FFFF33大域変数#FFFFFFというわ」\
r_show suzur_n
すずか「今までdefineで作っていた変数は大域変数なんだね」\
r_show ari_n
アリサ「それから、大域変数の束縛がある環境のことを#FFFF33大域環境#FFFFFFというわ。@/
それに対して、fの仮引数として使われているnはfの中でしか使えないの。@/
このような変数を#FFFF33局所変数#FFFFFFというわ」\
r_show suzur_excc
すずか「特定の範囲内でしか使えないものを局所変数というんだ」\
r_show ari_n
アリサ「変数の見える範囲を#FFFF33スコープ#FFFFFFというの。@/
大域変数のスコープは全体だけど、局所変数のスコープは一部といった感じね」\
r_show suzur_nc
すずか「大域変数のスコープは全体だからいいけど、局所変数のスコープは限られるわけだから、/
スコープをしっかりと意識した方がいいのかな」\
r_show ari_n
アリサ「その局所変数をつくりだす#33FF33let#FFFFFFというスペシャルフォームがあるの。
#33FF33/
(let ((変数名1 式1)
      (変数名2 式2)
      ・・・
      (変数名n 式n))
 式)#FFFFFF
letを評価すると、まず、式1~nを評価するの。@/
show_dgm ":l;img/dgm0701.bmp"
それから、新しい環境を作って、そこに変数名1と式1の値の束縛を追加、変数名2と式2の値の束縛を追加……とnまで繰り返すわ。@/
show_dgm ":l;img/dgm0702.bmp"
最後に、その新しい環境の後ろにletを評価した環境を繋げるの。@/
そして、その新しい環境で#33FF33式#FFFFFFを評価して、その結果を全体の結果とするの。@/
show_dgm ":l;img/dgm0703.bmp"
letを呼び終えたら、それ以降は元の環境で評価を行うから、変数名1~nはletの中のみで有効となるわけ。@/
つまり、letの中で作った変数のスコープはletの中に限るってことね」\
csp SP_DGM0
r_show suzur_nc
すずか「一時的に使う変数を作り出すわけだね。なんとなく分かったけど、使い道がいまいち分からないな……」\
r_show ari_n
アリサ「例えば、リストlstの2番目の要素をCAR、CDRの両方にもつコンスを作るとしましょう。
#33FF33/
(cons (car (cdr lst))
      (car (cdr lst)))#FFFFFF
見たら分かるけど、lstの2番目の要素を取り出す式が2回出てきてるわね。@/
同じ式を2回評価するのはあんまりかっこよくないでしょ。@/
そこでletを使うと、
#33FF33/
(let
 ((x (car (cdr lst))))
 (cons x x))#FFFFFF
この場合、lstの2番目の要素を取り出す式を一度だけ評価して、新しい環境でxに束縛。そのxを使って式を評価。@/
結果は両方とも同じだけど、こっちだと同じ計算を2回する手間が省けるでしょ」\
r_show suzur_n
すずか「なるほど、ちょっと便利だね」\
r_show ari_n
アリサ「けど『新しい環境を作ってそこに束縛を加える』っていう流れ。@/
これは関数のところでも同じことをいったわよね。@/
実は、この式はこんな風にも書けるの
#33FF33/
((lambda (x)
  (cons x x))
 (car (cdr lst)))#FFFFFF
lambdaで作った関数にlstの2番目の要素をつけて呼び出してるわ。@/
そうすると、新しい環境でxが束縛されて、letを使った場合と全く同じになるの」\
r_show suzur_excc
すずか「letを使った場合、xとそれを束縛するlstの2番目の要素の対応はパッと見て分かるけど、@/
lambdaを使った場合、仮引数xと実引数であるlstの2番目の要素が少し離れてるから分かりにくいね」\
r_show ari_exc
アリサ「同じ役割なのに、局所変数を作るうえではletの方が分かりやすいでしょ。@/
実はletはlambdaの糖衣構文なの。@/
lambdaは関数を作るだけだけど、letは呼び出しまでするから少し違うものに見えるかもしれないけど、/
letを使った式は全て、lambdaを使って書き直せるのよ」\
r_show suzur_n
すずか「引用符’とdefineの関数定義に続いて、3番目の糖衣構文だね」\
r_show ari_n
アリサ「今度はletを関数の中で使って見ましょう。
#33FF33/
(define f
 (lambda (n)
  (let
   ((m (+ n 1)))
   (* n m))))#FFFFFF
関数fの外側からnとmは見えないし、fの中でも、letの外側からはmは見えないわ。@/
show_dgm ":l;img/dgm0704.bmp"
つまり、外側からは内側の変数は見えないの。その一方で、内側からはどれだけ外側の変数も見えるのよ」\
csp SP_DGM0
r_show suzur_qes
すずか「分かったような分からないような……あれ? letってlambdaに書き換えれるんだよね。@/
だとすると、この式はlambdaの中にlambdaが出てくるように書き直せる!?」\
r_show ari_n
アリサ「いい所に気づいたわね。この式は次のように書き直せるわ。
#33FF33/
(define f
 (lambda (n)
  ((lambda (m)
    (* n m))
   (+ n 1))))#FFFFFF
ここで思い出して欲しいのが、lambdaの仕組み。@/
関数がlambdaを評価した時の環境を保持するっていうのがあったわよね」\
r_show suzur_dotc
すずか「えーと、まず、defineの引数として、外側のlambdaの式が評価される。@/
この時、出来上がる関数――すなわちfは大域環境を保持するんだね。@/
show_dgm ":l;img/dgm0705.bmp"
lambdaは引数を評価しないから、内側のlambdaの式はまだ評価されないよね。@/
ここで、#33FF33(f 16)#FFFFFFを評価すると、新しい環境が作られて、nが16で束縛される。@/
show_dgm ":l;img/dgm0706.bmp"
その後ろには関数が保持する環境――大域環境が繋がる。こうやって出来た環境を仮に環境Eと呼ぶね。@/
すると、その環境Eで引数として#33FF33(+ n 1)#FFFFFFが、関数として、内側のlambdaの式が評価されるんだから、/
ここで一時的に作られる関数は、環境Eを保持するんだね。@/
show_dgm ":l;img/dgm0707.bmp"
で、その関数を#33FF33(+ n 1)#FFFFFFの値17をつけて呼び出すと、また新しい環境が作られてmが17で束縛される。@/
その後ろには関数が保持する環境――環境Eが繋がる。@/
show_dgm ":l;img/dgm0708.bmp"
こうやって、最後に出来た環境はm、n、そして大域環境の全ての束縛が見れるんだ」\
csp SP_DGM0
r_show ari_ase
アリサ「よく一発で分かったわね……@これが内側からは外側が見えて、外側からは内側が見えない仕組みの正体だけど、@/
最初のうちは普通はあまり分からない場合もあるわ。@/
その場合は、#FFFF33内側からは外側が見える#FFFFFFということをしっかりと覚えておくといいわ」\
r_show suzur_qes
すずか「じゃあ、例によってそろそろ練習問題かな?」\
r_show ari_tun
アリサ「うーん……letってlambdaの便利な書き方に過ぎないから、問題を出すのもね……」\
r_show suzur_hrt
すずか「じゃあ、今回は問題は――」\
r_show ari_n
アリサ「今回はletは関係ない問題を出すわね」\
r_show suzur_ase
すずか「問題はないのかなと思わせぶりなことを言った直後にそれは酷いよ……」\
r_show ari_n
アリサ「問題に備えてリストの補足説明をするわね。@/
#33FF33null?#FFFFFFという関数があって、これは引数が空リストなら#t、そうでなければ#fを返すの。
#33FF33/
(null? ’()) ⇒#t
(null? ’(a))⇒#f#FFFFFF」\
r_show suzur_nc
すずか「#tか#fを返すってことは、ifやcondで使えそうな関数だね」\
r_show ari_n
アリサ「#tか#fを返す関数は#FFFF33述語#FFFFFFというの。@/
述語は疑問符?で終わる名前のものが多いのよ」\
r_show suzur_n
すずか「#33FF33null?#FFFFFFや#33FF33=#FFFFFFのことを述語って言うんだね」\
r_show ari_n
アリサ「空リストって言うのは『要素が0個のリスト』と考えれるわよね。@/
それから要素が一つのリストに対してcdrを使うと空リストが返るわ。@/
show_dgm ":l;img/dgm0709.bmp"
これはリストの図を描いたら分かるわよね。@/
それから思い出して欲しいのが、cdrはリストの先頭の要素を取り出したリストを作る関数と考えられるということ。@/
以上から、cdrは元のリストより要素が1つ少ないリストを作る関数とも考えられるの」\
csp SP_DGM0
gosub *ex_init
*ex07
mov $problem_label, "*ex07_problem"
mov $tab_label, "*ex07_tab"
mov $ret_label, "*ex07"
mov $suc_label, "*ex07_check"
goto *ex_rep
*ex07_problem
!s0アリサ「関数lenを『引数としてxを受け取ると、xの要素の数を返す関数』として定義しなさい。/
ただし、引数は1つで、リストのみが来ると考えていいわ」!sd
return
*ex07_check
gosub *pop : gosub *pop
;;;検証1
mov $sarg0, "(len '())"
gosub *input_to_lobject
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *eval_form
mov %arg0, %ret
gosub *get_data
mov $sarg0, "*ex07_miss"
if %ret != 0 goto *ex_check_fail
;;;検証2
mov $sarg0, "(len '(1 2 3 4 5))"
gosub *input_to_lobject
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *eval_form
mov %arg0, %ret
gosub *get_data
mov $sarg0, "*ex07_2_prev"
if %ret == 5 goto *ex_check_pass
mov $sarg0, "*ex07_miss"
goto *ex_check_fail
*ex07_miss
textclear
r_show ari_die
アリサ「違うわ。やり直し!」\
r_show ari_n
inc %adv_tmp
goto *ex07
*ex07_2_prev
mov $sarg0, "len"
gosub *create_symbol
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *find_var
mov %arg0, %ret
gosub *cdr
mov %arg0, %ret
gosub *push ;S(len)
*ex07_2
mov $problem_label, "*ex07_2_problem"
mov $tab_label, "*ex07_2_tab"
mov $ret_label, "*ex07_2"
mov $suc_label, "*ex07_2_check"
goto *ex_rep
*ex07_2_problem
!s0アリサ「引数#33FF33’(a b c)#FFFFFFをつけてlenを呼び出しなさい」!sd
return
*ex07_2_check
gosub *pop ;ret<S(input, len)
mov %arg0, %ret
gosub *pop ;input<S(len)
gosub *get_data
mov $sarg0, "*ex07_end"
if %ret == 3 gosub *pop : goto *ex_check_pass_end
;;;lenの再定義をcheck;;;
mov $sarg0, "len"
gosub *create_symbol
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *find_var
mov %arg0, %ret
gosub *cdr
mov %arg0, %ret
gosub *pop ;len<S()
mov $sarg0, "*ex07_2_miss"
if %arg0 != %ret goto *ex_check_fail
mov %arg0, %ret
gosub *push ;S(len)
mov $sarg0, "*ex07_2_normal_miss"
goto *ex_check_fail
;;;end;;;
*ex07_2_normal_miss
textclear
r_show ari_die
アリサ「違うわ。やり直し!」\
r_show ari_n
inc %adv_tmp
goto *ex07_2
*ex07_2_miss
textclear
r_show ari_muka
アリサ「……なかなか面白いことをやってくれるわね。@/
r_show ari_do
最初からやり直し!!!」\
r_show ari_n
inc %adv_tmp
goto *ex07
*ex07_tab
csel "続ける", *ex07, "ヒント", *ex07_hint, "タイトルに戻る", *ex07_bye
*ex07_2_tab
csel "続ける", *ex07_2, "ヒント", *ex07_2_hint, "タイトルに戻る", *ex07_2_bye
*ex07_bye
mov %adv_rmode, 1 ;右クリック有効
textclear
csp SP_R
stop
gosub *pop ;new env<S()
goto *title
*ex07_2_bye
gosub *pop ;l<S(new env)
goto *ex07_bye
*ex07_hint
textclear
アリサ「受け取る引数は一つだから#33FF33(lambda (x) 式)#FFFFFFと書くといいわ。@/
今回も前回同様に再帰を使うと上手くいきそうね。@/
リスト#33FF33(a1 a2…an)#FFFFFFの要素を数えるとしましょう。@/
nが0――つまり、空リストの時は要素数は当然0になるわ。@/
それ以外の時には要素数は、#33FF33(a2 a3…an)#FFFFFFの要素数に1を足したものになるわ。@/
このリストは元のリストのCDRを取ると作れるわよね。@/
つまり、lenの定義は、xが空リストなら#33FF330#FFFFFF、@それ以外なら、#33FF33(+ 1 (len (cdr x)))#FFFFFFと書けるわ。@/
xが空リストかどうかは#33FF33null?#FFFFFFを使うと確認できるわね。@/
あとは、ifかcondを使ってこれを式にするだけよ」\
goto *ex07
*ex07_2_hint
textclear
アリサ「単純に#33FF33(関数 引数)#FFFFFFの式を書けばいいわ」\
goto *ex07_2
*ex07_end
mov %adv_rmode, 1 ;右クリック有効
gosub *pop ;new env<S()
saveon
mov %save_flag, 1 ;セーブ可能
textclear
add %adv_miss, %adv_tmp
bgm ms_setsume
if %adv_tmp == 0 goto *s_07_2
if %adv_tmp >=2 goto *s_07_3
*s_07_1
アリサ「まあまあだったわね」\
goto *s_07_4
*s_07_2
r_show ari_hrt
アリサ「全く問題ないようね」\
goto *s_07_4
*s_07_3
r_show ari_ase
アリサ「ちょっと失敗が多いわね」\
goto *s_07_4
*s_07_4
r_show ari_n
アリサ「Schemeの変数のスコープは式を書いた時点で決まるでしょ。@/
このスコープの取り決めを、#FFFF33レキシカルスコープ#FFFFFFというの。@/
SchemeやCommonLispはレキシカルスコープだけど、/
elispをはじめとする、多くのLisp方言は式を評価するまでスコープの決まらない/
#FFFF33ダイナミックスコープ#FFFFFFというものが使われるの。@/
Lisp方言の二大勢力がレキシカルスコープを使ってるから、/
ここではダイナミックスコープについては割愛するわね」\
r_show suzur_nc
すずか「内側から外側は見えるけど、外側からは内側が見えないっていうのがレキシカルスコープだね」\
r_show ari_n
アリサ「今回の内容は、letを使うと局所変数が作れる、@/
letはlambdaの糖衣構文である、@/
lambdaの内部でlambdaを書いても問題はない、@/
その際の環境の作られ方に注意する。@/
といった感じね」\
r_show suzur_n
すずか「その割には練習問題は前回の再帰の続きみたいだったね」\
r_show ari_n
アリサ「再帰はLispでプログラムを書く上で最も重要ともいえるものだから、@/
しっかりと使えるようになって欲しいの。@/
だから、再帰の問題を出したのよ」\
r_show suzur_nc
すずか「確かに、再帰が分かることによって計算を#FFFF33繰り返す#FFFFFFことが出来るようになったしね」\
r_show ari_exc
アリサ「Lispで同じ計算を繰り返し行う時は、再帰を使うの。@/
他のプログラミング言語に詳しかったら『そんなことをしたら遅いし、メモリを食いつぶす』と思うかもしれないけど、大丈夫。@/
後で話すけど、Lispはそれに対する解決策があるから、安心して再帰を使っていいのよ」\
vsp SP_R, 0
bg black, E_NORMAL
r_load ari_n
l_load suzu_n
bgm ms_after
bg "img/blackboard.bmp", E_NORMAL
talk_mode
アリサ「そういえばさ、火星人はLispを使
    っているって話知ってる?」\
l_show suzu_dot
すずか「なんか、火星人がいるのが前提ってい
    うのが凄いね……」\
r_show ari_tun
アリサ「京都のλ山は有名だけど、実は火星に
    もね……あるのよ」\
l_show suzu_excc
すずか「ま、まさか!」\
r_show ari_exc
アリサ「火星のクレータにはλの文字がある!
    この文字こそ火星人がLispを使っ
    ている証拠なのよ!!」\
l_show suzu_exc
すずか「な・・・・なんだってー」\
r_show ari_n
アリサ「まあ、火星人がいればの話だけどね」\
l_show suzu_ase
すずか「とりあえず驚いてみたけど、火星人が
    いても、その話の進め方には無理があ
    ると思うな……」\
;;;;;第7話終了;;;;;
speak_mode
textclear
vsp SP_R, 0
vsp SP_L, 0
bg black, E_SLOW
;;共通記録の書き換え
csvopen "arisa.szk", "rc"
csvread %adv_clear
csvclose
if %adv_clear >= 7 goto *story07_after
mov %adv_clear, 7
csvopen "arisa.szk", "wc"
csvwrite %adv_clear
csvclose
#FFFF33補足説明#FFFFFFが追加されました。
#FFFF33用語集#FFFFFFに単語が追加されました。\
*story07_after
r_show suzur_nc
すずか「どうしようかな?」
csel "補足説明を読む", *story07_condicil, "用語集を読む", *story07_glossary, "タイトルに戻る", *title_back, "第8話へ進む", *story08
*story07_condicil
gosub *condicil
goto *story07_after
*story07_glossary
gosub *glossary
goto *story07_after
;;;;;第8話;;;;;
*story08
mov %adv_noroi_off, 0
csp SP_R : csp SP_L : print E_FAST
stop
talk_mode
bgm ms_kyoto
bg "img/blackboard.bmp", E_FAST
r_in ari_n
l_in suzu_n
すずか「お医者さんに行って貰う処方箋って、
    ものすごく丁寧に書いてある場合があ
    るよね」\
アリサ「薬の写真がカラーで入ってたり、薬を
    飲む時間とかが詳しく書いてたりする
    わね」\
l_show suzu_ase
すずか「薬の副作用についてなんかも、結構書
    いてあって、なんか妙に心配しちゃっ
    たりするんだよね」\
csp SP_R : csp SP_L : print 1
bg "img/title#08.bmp", E_VFAST
wait 1000
delay 2000
bg "img/blackboard.bmp", E_FAST
bgm ms_setsume
speak_mode
r_show ari_n
アリサ「変数の値の更新というやつをやってみましょう」\
r_show suzur_qes
すずか「これまで、変数は一度定義したらその値が変わることはなかったよね。@/
変数の値を変えるの?」\
r_show ari_n
アリサ「まあ、とりあえず、例を見てみましょう。
#33FF33/
(define x 9)
(set! x 19)#FFFFFF
この2つ式を順番に評価した後に#33FF33x#FFFFFFを評価すると、結果は19になるわ」\
r_show suzur_n
すずか「xの値が9から19に変化してるね。@/
つまり、#33FF33(set! x 19)#FFFFFFはxを19で束縛し直すってことかな」\
r_show ari_n
アリサ「ちょっと違うわ。@/
実は今まで使っていた#FFFF33変数が値に束縛される#FFFFFFというのは正確な表現ではないの。@/
正確には、変数は『値をひとつだけ格納できる箱』に束縛されるの。@/
そして、その箱の中に変数の値が代入されるの。@/
変数の値の更新をしない場合は変数と値が直接結びついてると考えていいけど、@/
変数の値の更新をする場合は変数と箱が結びついていて、/
その箱の中に変数の値が入っていると考えないといけないわ」\
r_show suzur_n
すずか「それじゃあ、set!は、その箱の中身を書き換えるんだね」\
r_show ari_n
アリサ「これまでの内容をしっかり理解できてるなら分かってると思うけど、#33FF33set!#FFFFFFはスペシャルフォームよ。@/
第一引数のxが評価されずにシンボルのまま扱われているのを見たらわかるわね。@
#33FF33(set! 変数名 式)#FFFFFF
これで変数と結びついた箱の中身を変更できるわ。/
ただし、set!は変数の値を変更するだけで、/
新たに変数を作り出すわけではないから注意してね」\
r_show suzur_nc
すずか「set!を使う前に変数を作っておく必要があるんだね」\
r_show ari_n
アリサ「それから、set!は変数の値を変更するところに意味があって、その式の値は特に意味を持たないの。@/
NScLisperではset!を呼び出すと#tを返すことにしてあるけど、この結果を利用したりするのは好ましくないわ。@/
このset!のように、結果である値には意味がなく、その効果に意味があるものを#FFFF33副作用#FFFFFFのある操作というのよ」\
r_show suzur_dotc
すずか「set!の返す値を使っちゃいけないんだね。じゃあ、
#33FF33/
(define (set-x! m n)
 (set! x (+ m n)))#FFFFFF
こんな風にset!を使う関数の返す値も使っちゃ駄目なんだね……@/
変更後のxの値が関数の返す値だったら便利なんだけどな」\
r_show ari_n
アリサ「そういう場合#33FF33begin#FFFFFFというスペシャルフォームを使って、
#33FF33/
(define (set-x! m n)
 (begin
  (set! x (+ m n))
  x))#FFFFFF
とすれば、関数の返す値は変更後のxの値となるわ。@/
beginは任意個の引数を取って、第一引数から順番に評価していって、最後の引数を評価した結果を全体の値とするスペシャルフォームなの」\
r_show suzur_excc
すずか「引数を全て評価するけど、最後の引数の値しか使わないから、途中に副作用のある式を入れるのには便利なんだね」\
r_show ari_n
アリサ「実は、関数定義の時には暗黙にbeginが使われていて、
#33FF33/
(define (set-x! m n)
 (set! x (+ m n))
 x)#FFFFFF
こんな風に書けるの。@/
もちろん、lambdaを使う時も同様に
#33FF33/
(define set-x!
 (lambda (m n)
  (set! x (+ m n))
  x))#FFFFFF
のような書き方が出来るわ」\
r_show suzur_nc
すずか「つまり、関数定義の時には任意個の式を書いてよくて、その場合、式は順番に評価されていって、/
最後の式の値が関数全体の結果として使われるんだ」\
r_show ari_n
アリサ「これはlambdaの糖衣構文であるletも同様に、
#33FF33/
(let (・・・)
 式1
 ・・・
 式n)#FFFFFF
と書けるわ。
これは、新しい環境の中で式1~nを順番に評価して、式全体の結果は式nを評価した値となるわ」\
r_show suzur_n
すずか「letはlambdaと実質的に同じだから、同じことが出来るんだね」\
r_show ari_n
アリサ「さらに、condでも同じことができるの。
#33FF33
(cond (条件式 実行式1
           ・・・
           実行式n)
      ・・・)#FFFFFF
こう書くと、条件式を評価した結果が真だった場合、実行式1~nが順番に評価されて、/
式全体の結果は、実行式nを評価した値となるの」\
r_show suzur_dotc
すずか「この流れのままいくとifもできるのかな。/
えーと、ifの使い方は……
#33FF33/
(if 条件式
    式A
    式B)#FFFFFF@
r_show suzur_qes
あれ? ifは引数を3つしかとれないから、同じようなことが出来ない?」\
r_show ari_n
アリサ「残念ながらifは条件式が真の時の式、偽の時の式もそれぞれ一つずつしか書けないわ。@/
複数個式を書きたかったら、condを使うか、ifの中でbeginを使えばいいわ」\
r_show suzur_nc
すずか「えーと、
#33FF33/
(if 条件式
    (begin
     式A1
     ・・・
     式An)
    ・・・)#FFFFFF
みたいな感じかな」\
r_show ari_n
アリサ「話をset!に戻しましょう。少し前に出てきた関数
#33FF33/
(define (set-x! m n)
 (set! x (+ m n))
 x)#FFFFFF
これの意味を少し考えてみましょうか」\
r_show suzur_nc
すずか「えーと、この関数set-x!は引数を2個受け取って、xの値をその和に変更するんだよね。@/
ただ、これをやる前にdefineでxを定義しておく必要があるのかな」\
r_show ari_n
アリサ「そう。ここで触ってるxは関数の外側にあるxのことなの。
#33FF33/
(define x 0)
(define (set-x! m n)
 (set! x (+ m n))
 x)
x            ⇒0
(set-x! 9 19)⇒28
x            ⇒28#FFFFFF
この結果から、外側のxの値が変化してるのがわかるわね」\
r_show suzur_n
すずか「確か内側からは外側を触れるんだよね。これは変数の値を変更する時も同じなんだ」\
r_show ari_n
アリサ「これを少し変化させて、
#33FF33/
(define x 0)
(define (set-x2! x n)
 (set! x (+ x n))
 x)
x            ⇒0
(set-x! 9 19)⇒28
x            ⇒0#FFFFFF
こうすると、外側のxは変化しないでしょ」\
r_show suzur_dotc
すずか「えーと、この場合は、set-x2!の引数としてxという名前が使われてるから、/
そのxの方が外側のxより優先されるんだよね。@/
r_show suzur_qes
だから、set!による値の更新は、外側のxじゃなくて、内側のxに対して行われたってこと?」\
r_show ari_exc
アリサ「その通り! これを応用すると面白いことが出来るの。@/
けど、その前にちょっと復習をしましょう。@/
letのところで、lambdaの中にlambdaを書く例をやったわよね」\
r_show suzur_n
すずか「あったね。内側からは外側が見えるけど、外側からは内側は見えないというやつだよね」\
r_show ari_n
アリサ「あの例では、内側のlambdaで作った関数はその場で呼び出してたけど、こんなこともできるの。
#33FF33/
(define ma
 (lambda (n)
  (lambda (x)
   (+ n x))))#FFFFFF
これはどういった関数だと思う?」\
r_show suzur_dotc
すずか「外側のlambdaを見ると、引数を一つ受け取る関数だよね。@/
で、その内容は、lambdaの呼び出し――つまり、関数を作って返すというものだから……@/
r_show suzur_exc
関数maを呼び出した結果は新しい関数!?」\
r_show ari_n
アリサ「式を評価したら、値が返ってくる。関数も値の一種なんだから別におかしなことじゃないでしょ。@/
lambdaは関数を返すスペシャルフォーム。同じように、maも関数を返す関数ってだけよ」\
r_show suzur_ase
すずか「えーと、内側のlambdaを見ると、引数を1つ受け取って、それとnの和を返す関数を作ってるんだね。@/
……うーん、使い方がよく分からないよ」\
r_show ari_n
アリサ「じゃあ、実際に使ってみましょう
#33FF33/
(define a5 (ma 5))⇒a5
(define a9 (ma 9))⇒a9
(a5 5)⇒10(=5+5)
(a9 5)⇒14(=9+5)
(a5 1)⇒6 (=5+1)
(a9 1)⇒10(=9+1)#FFFFFF@
a5とa9は新しく作った関数で束縛されてるわけだけど、/
結果を見たら分かるように、それぞれで使われているnという値がしっかりと残っているというのが重要よ」\
r_show suzur_nc
すずか「nっていうのは関数maの仮引数だよね。maの呼び出しが終わったあとも、/
内側のlambdaで作られる関数で使われ続けてるんだ」\
r_show ari_n
アリサ「これは、関数が環境を覚えるからこんなことができるのよ。@/
例えばa5の場合、maの内側のlambda評価を評価した/
環境――すなわち、『nは5』という環境を覚えてるわけだから、上手くいくの。/
a9の場合も全く同じね」\
r_show suzur_qes
すずか「なんだかややこしいね。ところで『面白いこと』っていうのは何なの?」\
r_show ari_n
アリサ「この例とset!を組み合わせるの。
#33FF33/
(define ma2
 (lambda (n)
  (lambda (x)
   (set! n (+ n x))
   n)))#FFFFFF
こんどは記憶しているnの値を変更しているの。/
さあ、これをさっきと同じように使ってみましょうか」\
r_show suzur_nc
すずか「えーと、
#33FF33/
(define a4 (ma2 4))
(a4 8)⇒12(=4+8)
(a4 8)⇒20(=12+8)
(a4 8)⇒28(=20+8)
(a4 8)⇒36(=28+8)#FFFFFF
引数に8をつけて呼び出すと、結果は8ずつ増えていくね。@/
関数に記憶されてるnの値がちゃんと変更されてるんだ」\
r_show ari_n
アリサ「ちなみに、この関数は、
#33FF33/
((ma2 4) 8)⇒12#FFFFFF
みたいに使うこともできるわ。@/
関数を返す関数に関しては全て、
#33FF33/
((関数 引数) 引数)#FFFFFF
という書き方が許されるの。@/
ただし、これはSchemeの場合の話で、Lisp方言によってはこの書き方ができないから注意してね」\
r_show suzur_n
すずか「Schemeでは関数を返す関数は、lambdaみたいな書き方ができるってことだね」\
r_show ari_die
アリサ「set!は便利なんだけど、乱用すると混乱を生む恐れもあるから、使いどころは考えた方がいいわね」\
r_show suzur_excc
すずか「副作用には注意しろってことだね」\
r_show ari_n
アリサ「さて、そろそろ練習問題をやりましょうか」\
gosub *ex_init
*ex08
mov $problem_label, "*ex08_problem"
mov $tab_label, "*ex08_tab"
mov $ret_label, "*ex08"
mov $suc_label, "*ex08_check"
goto *ex_rep
*ex08_problem
!s0アリサ「関数fを『引数としてxを受け取ると、”#33FF33引数を1つ取り、xとその和を返す関数#FFFFFF”を返す関数』として定義しなさい。/
ただし、引数は1つで、数のみが来ると考えていいわ」!sd
return
*ex08_check
gosub *pop : gosub *pop
;;;検証1
mov $sarg0, "((f 9) 99)"
gosub *input_to_lobject
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *eval_form
mov %arg0, %ret
gosub *get_data
mov $sarg0, "*ex08_miss"
if %ret != 108 goto *ex_check_fail
;;;検証2
mov $sarg0, "((f 2) 5)"
gosub *input_to_lobject
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *eval_form
mov %arg0, %ret
gosub *get_data
mov $sarg0, "*ex08_2_prev"
if %ret == 7 goto *ex_check_pass
mov $sarg0, "*ex08_miss"
goto *ex_check_fail
*ex08_miss
textclear
r_show ari_die
アリサ「違うわ。やり直し!」\
r_show ari_n
inc %adv_tmp
goto *ex08
*ex08_2_prev
mov $sarg0, "f"
gosub *create_symbol
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *find_var
mov %arg0, %ret
gosub *cdr
mov %arg0, %ret
gosub *push ;S(f)
*ex08_2
mov $problem_label, "*ex08_2_problem"
mov $tab_label, "*ex08_2_tab"
mov $ret_label, "*ex08_2"
mov $suc_label, "*ex08_2_check"
goto *ex_rep
*ex08_2_problem
!s0アリサ「#33FF33(f 6)#FFFFFFが返す関数に引数#33FF3366#FFFFFFをつけて呼び出しなさい」!sd
return
*ex08_2_check
gosub *pop ;ret<S(input, f)
mov %arg0, %ret
gosub *pop ;input<S(f)
gosub *get_data
mov $sarg0, "*ex08_end"
if %ret == 72 gosub *pop : goto *ex_check_pass_end
;;;fの再定義をcheck;;;
mov $sarg0, "f"
gosub *create_symbol
mov %arg0, %ret
mov %arg1, %toplevel_env
gosub *find_var
mov %arg0, %ret
gosub *cdr
mov %arg0, %ret
gosub *pop ;f<S()
mov $sarg0, "*ex08_2_miss"
if %arg0 != %ret goto *ex_check_fail
mov %arg0, %ret
gosub *push ;S(f)
mov $sarg0, "*ex08_2_normal_miss"
goto *ex_check_fail
;;;end;;;
*ex08_2_normal_miss
textclear
r_show ari_die
アリサ「違うわ。やり直し!」\
r_show ari_n
inc %adv_tmp
goto *ex08_2
*ex08_2_miss
textclear
r_show ari_muka
アリサ「……なかなか面白いことをやってくれるわね。@/
r_show ari_do
最初からやり直し!!!」\
r_show ari_n
inc %adv_tmp
goto *ex08
*ex08_tab
csel "続ける", *ex08, "ヒント", *ex08_hint, "タイトルに戻る", *ex08_bye
*ex08_2_tab
csel "続ける", *ex08_2, "ヒント", *ex08_2_hint, "タイトルに戻る", *ex08_2_bye
*ex08_bye
mov %adv_rmode, 1 ;右クリック有効
textclear