Land of Lisp
(オライリージャパン)
M.D. Conrad Barski (著) 川合 史朗 (翻訳)
原書: Land of LISP
Learn to Program in Lisp, One Game at a Time!
開発環境
- OS X Yosemite - Apple (OS)
- Emacs(Text Editor)
- Scheme (プログラミング言語)
- kscheme, Gauche, MIT/GNU Scheme, GNU Guile (処理系)
Land of Lisp (M.D. Conrad Barski (著)、川合 史朗 (翻訳)、オライリージャパン)の7章(単純なリストの先へ)、7.4(無向グラフを作る)を Scheme で取り組んでみる。
7.4(無向グラフを作る)
コード(Emacs)
(begin
(define odd? (lambda (n) (= (remainder n 2) 1)))
(define map
(lambda (proc items)
(if (null? items)
(quote ())
(cons (proc (car items))
(map proc (cdr items))))))
(define for-each
(lambda (proc items)
(if (not (null? items))
(begin (proc (car items))
(for-each proc (cdr items))))))
(define assoc
(lambda (obj alist)
(if (null? alist)
#f
(let ((item (car alist)))
(if (equal? obj (car item))
item
(assoc obj (cdr alist)))))))
(define print (lambda (x) (display x) (newline)))
(define *house* (quote ((walls (mortar (cement)
(water)
(sand)))
(windows (glass)
(frame)
(curtains))
(roof (shingles)
(chimney)))))
(define *wizard-nodes*
(quote ((living-room (You are in the living-room. There is a wizard is
snoring loudly on the couch.))
(garden (You are in a beatiful garden. There is a well in front of
you.))
(attic (You are in the attic. There is a giant welding torch in
the corner.)))))
(define *wizard-edges* (quote ((living-room (garden west door)
(attic upstairs ladder))
(garden (living-room east door))
(attic (living-room downstairs ladder)))))
;; Common Lisp にはあっても、Scheme にない手続きがあったり、kscheme には、
;; Scheme の仕様にはあっても、実装してない手続きがまだたくさんあったりするので、
;; とりあえず、同様に機能する必要な手続きを定義
;; Scheme は (quote 24)は記号 (symbol) ではなく、数値(number) になるみたい。
;; ということで、symbol->string で (quote 24) を文字列にできない
;; (symbol? (quote 24)) は偽(#f)
(define object->string
(lambda (o)
(cond ((symbol? o) (symbol->string o))
((number? o) (number->string o))
((null? o) "")
((pair? o) (string-append (object->string (car o))
(object->string (cdr o))))
((string? o) o))))
(define substitute-if
(lambda (o pred exp)
(if (string? exp)
(list->string (map (lambda (c1)
(if (pred c1)
o
c1))
(string->list exp)))
(map (lambda (item)
(if (pred item)
o
item))
exp))))
(define complement
(lambda (pred)
(lambda (x)
(not (pred x)))))
(define alphanumeric?
(lambda (x)
(or (char-alphabetic? x)
(char-numeric? x))))
(define dot-name
(lambda (exp)
(substitute-if #\_
(complement alphanumeric?)
(object->string exp))))
(define *max-label-length* 30)
(define substring
(lambda (s start end)
(list->string (string->list s start end))))
(define string-append
(lambda (s1 s2)
(list->string (append (string->list s1)
(string->list s2)))))
(define object->string-1
(lambda (o)
(cond ((null? o) "")
((pair? o)
(string-append (object->string-1 (car o))
(object->string-1 (cdr o))))
(else
(string-append
(cond ((symbol? o) (symbol->string o))
((number? o) (number->string o))
((string? o) o))
" ")))))
(define dot-label
(lambda (exp)
(if exp
(let ((s (object->string-1 exp)))
(if (> (string-length s) *max-label-length*)
(string-append (substring s 0 (- *max-label-length* 3))
"...")
s))
"")))
(define out-file (open-output-file "sample3_1.txt"))
(define nodes->dot
(lambda (nodes port)
(map (lambda (node)
(newline port)
(display (dot-name (car node)) port)
(display "[label=\"" port)
(display (dot-label node) port)
(display "\"];" port))
nodes)))
(define edges->dot
(lambda (edges port)
(map (lambda (node)
(map (lambda (edge)
(newline port)
(display (dot-name (car node)) port)
(display "->" port)
(display (dot-name (car edge)) port)
(display "[label=\"" port)
(display (dot-label (cdr edge)) port)
(display "\"];" port))
(cdr node)))
edges)))
(define graph->dot
(lambda (nodes edges port)
(display "digraph{" port)
(nodes->dot nodes port)
(edges->dot edges port)
(display "}" port)))
;; Common Lisp と違って、Scheme の仕様に、C言語の system 関数みたいな、
;; 外部スクリプトを実行する手続きがないっぽかった(?)。
;; ということで、kscheme に独自に system 手続きを実装。
;; Gauche には、C言語の exec 関数に相当する、sys-exec 等の手続き、
;; Guile には system 手続きがあった。
(define dot->png
(lambda (fname proc)
(proc)
(system (string-append "dot -Tpng -O " fname))))
(define graph->png
(lambda (fname nodes edges)
(dot->png fname
(lambda ()
(let ((out-file (open-output-file fname)))
(graph->dot nodes edges out-file)
(close-output-port out-file))))))
(define maplist
(lambda (proc items)
(if (not (null? items))
(begin (proc items)
(maplist proc (cdr items))))))
;; Common Lisp の mapcar は Scheme の for-each 手続きと同じっぽい(?)
(for-each print (quote (a b c)))
(newline)
(print (maplist print (quote (a b c))))
(define uedges->dot
(lambda (edges port)
(maplist (lambda (items)
(map (lambda (edge)
(if (not (assoc (car edge) (cdr items)))
(begin (newline port)
(display (dot-name (caar items)) port)
(display "--" port)
(display (dot-name (car edge)) port)
(display "[label=\"" port)
(display (dot-label (cdr edge)) port)
(display "\"];" port))))
(cdar items)))
edges)))
(define ugraph->dot
(lambda (nodes edges port)
(display "graph{" port)
(nodes->dot nodes port)
(uedges->dot edges port)
(display "}" port)))
(define ugraph->png
(lambda (fname nodes edges)
(dot->png fname
(lambda ()
(let ((out-file (open-output-file fname)))
(ugraph->dot nodes edges out-file)
(close-output-port out-file))))))
(ugraph->png "uwizard.dot" *wizard-nodes* *wizard-edges*)
(quote done))
入出力結果(Terminal(kscheme), REPL(Read, Eval, Print, Loop))
$ kscheme sample4.scm a b c (a b c) (b c) (c) #<undefined> done $ cat uwizard.dot graph{ living_room[label="living-room You are in the l..."]; garden[label="garden You are in a beatiful..."]; attic[label="attic You are in the attic. ..."]; garden--living_room[label="east door "]; attic--living_room[label="downstairs ladder "];}$ open uwizard.dot.png $
DOT ファイルから、Graphviz を使って得た無向グラフ。
![無向グラフ />
<!-- zenback_date 2015-07-12 -->
<!-- google_ad_section_end -->
<!-- rakuten_ad_target_end -->
<!-- zenback_body_end -->
<div style='clear: both;'>
</div>
</div>
<div class='post-footer'>
<div class='post-footer-line post-footer-line-1'>
<span class='post-author vcard'>
</span>
<span class='post-timestamp'>
時刻:
<a class='timestamp-link' href='https://www.mkamimura.com/2015/07/scheme-land-of-scheme_12.html' rel='bookmark' title='permanent link'>
<abbr class='published' title='2015-07-12T12:00:00+09:00'>
12:00
</abbr>
</a>
</span>
<span class='post-labels'>
ラベル:
<a href='https://www.mkamimura.com/search/label/Lisp' rel='tag'>
Lisp
</a>
,
<a href='https://www.mkamimura.com/search/label/Programming' rel='tag'>
Programming
</a>
,
<a href='https://www.mkamimura.com/search/label/Scheme' rel='tag'>
Scheme
</a>
</span>
<span class='post-comment-link'>
</span>
<span class='item-control blog-admin pid-661785034'>
<a href='https://www.blogger.com/post-edit.g?blogID=8580207075494340578&postID=8696436710667549037&from=pencil' title='投稿を編集'>
<img alt='' class='icon-action' height='18' src='https://img2.blogblog.com/img/icon18_edit_allbkg.gif' width='18'/>
</a>
</span>
</div>
<div class='post-footer-line post-footer-line-3'>
</div>
</div>
</div>
<div class='comments' id='comments'>
<a name='comments'></a>
<h4>
0
コメント:
</h4>
<div id='Blog1_comments-block-wrapper'>
<dl class='avatar-comment-indent' id='comments-block'></dl>
</div>
<p class='comment-footer'>
<div class='comment-form'>
<a name='comment-form'></a>
<h4 id='comment-post-message'>
コメントを投稿
</h4>
<p>
</p>
<a href='https://www.blogger.com/comment/frame/8580207075494340578?po=8696436710667549037&hl=ja&saa=47563' id='comment-editor-src'></a><iframe allowtransparency='true' class='blogger-iframe-colorize blogger-comment-from-post' frameborder='0' height='410' id='comment-editor' name='comment-editor' src='' width='100%'></iframe><!--Can't find substitution for tag [post.friendconnectjs]--><script src='https://www.blogger.com/static/v1/jsbin/1839367302-comment_from_post_iframe.js' type='text/javascript'></script>
<script type='text/javascript'>
BLOG_CMT_createIframe('https://www.blogger.com/rpc_relay.html', '0');
</script>
</div>
</p>
<div id='backlinks-container'>
<div id='Blog1_backlinks-container'>
</div>
</div>
</div>
</div>
</div></div>
<!--Can't find substitution for tag [adEnd]-->
</div>
<div class='blog-pager' id='blog-pager'>
<span id='blog-pager-newer-link'>
<a class='blog-pager-newer-link' href='https://www.mkamimura.com/2015/07/scheme-c-scheme-interpreter-kscheme_12.html' id='Blog1_blog-pager-newer-link' title='次の投稿'>
次の投稿
</a>
</span>
<span id='blog-pager-older-link'>
<a class='blog-pager-older-link' href='https://www.mkamimura.com/2015/07/scala-core-scala-expressions-and.html' id='Blog1_blog-pager-older-link' title='前の投稿'>
前の投稿
</a>
</span>
<a class='home-link' href='https://www.mkamimura.com/'>
ホーム
</a>
</div>
<div class='clear'></div>
<div class='post-feeds'>
<div class='feed-links'>
<!--Can't find substitution for tag [feedlinksmsg]--><a class='feed-link' href='https://www.mkamimura.com/feeds/8696436710667549037/comments/default' target='_blank' type='application/atom+xml'>コメントの投稿(Atom) </a>
</div>
</div>
</div><div class='widget HTML' data-version='1' id='HTML5'>
<div class='widget-content'>
<script async=](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjy5ukDlAfEHJ97RsKxEmpJH2iE5G_mVSHGQesfwH-MebFU5h8B90vEpubtjHnJYZ-4UZ-Yhz8Bd-3XDFFWduSi0Nb67FHBCodrESHulyhJgFjo4NBIfAibHSOnSLKyrQjZzD5rkHLsQrwr/s1600/uwizard.dot.png)