#(lambda (x s)`(,x ,s)) は list でいいかな
#quasiquot を使ってるけど、この場合は list* にした方が無駄なコピーがおこらないと思う。
#`(,(car kvl)
,(rec (cadr kvl) (cdr keyl) nv '0)
,@(rec (cddr kvl) keyl nv (+ n 1)))
#このかわりに
#(list* (car kvl)
(rec (cadr kvl) (cdr keyl) nv '0)
(rec (cddr kvl) keyl nv (+ n 1)))
#こう。
#なるほど、ありがとうございます。quiasiquoteはコピーが発生するんですね。
#りあえず、下のように書き換えてみました。
#(define (ptlPutKvListValue keyList newValue kvList)
(define (rec kvl keyl nv n)
(cond ((eq? kvl '())
(cond ((= n 0)
(fold (list x s)
nv (reverse keyl)))
(else
'())))
((eq? (car keyl) (car kvl))
(cond ((eq? (cdr keyl) '())
(list* (car kvl)
nv
(rec (cddr kvl) keyl nv (+ n 1))))
(else
(list* (car kvl)
(rec (cadr kvl) (cdr keyl) nv '0)
(rec (cddr kvl) keyl nv (+ n 1))))))
(else
(list* (car kvl) (cadr kvl) (rec (cddr kvl) keyl nv n)))))
(rec kvList keyList newValue 0))
(define put-kv ptlPutKvListValue)
#問題なく動きます。
#あれ?キーの追加がうまく動かない・・・
#今朝は時間切れなので、後で見直してみます・・・
#思ったんですけど、kv-listでなくalist使ったほうが簡単なんじゃないでしょうか。
#(use util.list)
(define (get-alist keys alist . rest)
(let-optionals* rest ((default #f))
(if (null? keys)
alist
(let ((pair (assoc (car keys) alist)))
(if pair
(apply get-alist (cdr keys) (cdr pair) rest)
default)))))
(define (put-alist keys alist val)
(assoc-set! alist (car keys)
(if (null? (cdr keys))
val
(put-alist (cdr keys) (assoc-ref alist (car keys) '()) val))))
#こんな感じに使います。
#gosh> (get-alist '(foo bar) '((foo . ((bar . #f) (baz . 2))) (quux . 3)) 'none)
#f
gosh> (put-alist '(foo bar) '((foo . ((bar . #f) (baz . 2))) (quux . 3)) 1)
((foo (bar . 1) (baz . 2)) (quux . 3))
#キーの追加もできます。
#gosh> (put-alist '(aaa bbb) '((foo . ((bar . #f) (baz . 2))) (quux . 3)) 4)
((aaa (bbb . 4)) (foo (bar . #f) (baz . 2)) (quux . 3))
#私も alist の方が簡単だと思います。
#コメントにある例だと、 :list の項が ("1" "2") ですけど
#これが配列か kv-list かをどう区別するべきでしょうか?
#今オフィスに戻ってきました。今日は「立川志の輔独演会」を見に行って、そのまま自宅で焼肉パーティでした。
#ツッコミありがとうございます。
#確かにa-listの方がシンプルになりそうなのですが、KV-Listの方が認知しやすいという理由から、KV-Listを採用したという経緯があります。a-listは括弧が多くなる分、階層的なデータ構造になったときに、中間結果の認知が追いつかないんです。まあこれは、私の認知能力の問題でもあるのですが。
#でも確かに
#a-listの方がシンプルそうですね。
#う~む、どうしようかな・・・。
#私のコードとa-listのアプローチと、どれだけコストに差があるのかなあ。
#これはちょっと興味があります。コストの差が大きいのであれば変更を検討すべきですし、以外と小さいのでれば認知コストを優先する、という考え方もありますよね・・・
#でも確かにalistの方がシンプルですねえ。
#表現に必要なコンスセルの数は同じはずですよね。
#でも見た目がけっこう違いますよね・・・
#alist の方が、どれとどれが key と value のペアになっているのかがわかりやすいとも言えます。 区切りのために入る括弧で見た目のスッキリ感が失われるのはデメリットですが…。
#alist なら assoc とかの既存のリスト処理関数とも相性が良いというか、用意されているだけあって Schemer は割と alist に慣れている人が多いのかもしれません。
#確かにそれは、そうなんですよね。KV-Listを使う論理的な理由は、確かに薄いかも。Pettal以前のプログラムでは、私もalistを使ってたので。でもなんとなく、パッと見たときに、KV-Listの方が短時間で理解できそうだって思っちゃうんですよね。
#まあこれは、本質的な問題ではないのかもしれません・・・
#Common Lisp なら plist 用の関数も用意されてるみたいですね。
#言語の思想に逆らわないように(言語仕様・ライブラリにあるものを使うように)した方がだいたい楽できる気はします。
#とは言うものの、 Lisp 系言語は既存の枠からはみ出すときにこそ力を発揮する側面もあるわけですが。
#Gauche使ってて感じるのは、わがままな発想を具現化するのが楽だということなんですよねえ。ブログにも上げている自分仕様のList->JSONとか、get-kv/put-kvなんかも、ツメが甘いところは多々あるのですが、それなりに使えるものが1~2時間でできてしまう、っていうのが魅力です。
#何か技術的な選択をゆる~くできるっているのは、大きな利点かも。技術的な選択って、実際にコードを書くコトよりも、はるかにむずかしいような気がします。
#とりあえずガッと作ってから要るところを修正していくってスタイルですね。
#とりあえず動くようになったものを動かしながら修正するってのは Lisp っぽい感じがします。
#そうそう、そうです。
#実際には、普段はシビアな選択が必要なところはあんまりないので、割と汚いまま放置しがち…
#ははは・・・
#でも選択しなければならないところって他にもいろいろあるので、「まあこれでいけるんじゃないかな」ってところは、確たる理由もなくそのまま前進するケースは多いかもしれません。
#一応後から修正しやすいようにひとつひとつの定義は小さ目にするというのだけは普段からやってる気がする。
#それは重要ですよね。定義が大きくなるとネーミングも難しくなりますよね。
#定義が大きくなるとネーミングが難しくなるというより、適当に拡張するとネーミングに対応できる抽象概念とのマッピングが難しくなるというべきか・・・
#私の目安は80桁×20行あたりかな。 ひとつの関数定義がこれよりデカいとちょっと複雑すぎる気分になる。 まぁ目安なので例外も多いですけど。
#MS-DOS 時代の一画面の大きさが染み付いているのだと思います。
#言語にもよりますよね。Gaucheでは私も20行くらいを意識しているかも。いや、30行くらいの関数もありますね。emacsで上下分割して下のウィンドウにコードを記述した場合、30行がスクロールなしで表示できる限界なので、このあたりかな?Javascriptのコーディングでは独自のコーディングシステムを作っているので、50行くらいのものも少なくありません。でも「手のひらに乗る程度」っていう感覚は、重要だと感じています。私自身はMS-DOSの画面サイズは意識したことがないかも。20年前からX-Windowsだったし、メガペルディスプレイなら80行くらいはx-termで表示できたから。
#20年まえでその環境を使ってたというとプロですか? 結構高価なものに思いますが。
#当時はプロだったと思います。ほとんどC言語でしたが。今はアマです。甘アマです。
#20年前はLispは理解できませんでしたが、Gaucheと出会って理解できたような気がしました。なんだか再出発している気分です。でもたのし~。
#20年前の私は小学生ですが、普通のパソコンでもそれほどありふれたものではなかったように思います。 そう考えると今はいい時代ですよねぇ。
#Linuxが普及したのが大きいですよね。1990年代前半にLinux0.9をCD-ROMで入手してインストールしたのが最初でしたが、それでもあの頃はUNIX(AIXとHP-UX、Sun-OS)でないと、使い物にならないといった感じでした。昔のSun-OSってCTRL-Aでシングルユーザーモードになったんですよ。信じられますか?ハッキングし放題。でもぜったい今の方がプログラムのスタディにはいい時代です。
#すみません、前述の私のコード、ポカミスでした。
#(define (ptlPutKvListValue keyList newValue kvList)
(define (rec kvl keyl nv n)
(cond ((eq? kvl '())
(cond ((= n 0)
(fold (lambda (x s) (list x s))
nv (reverse keyl)))
(else
'())))
((eq? (car keyl) (car kvl))
(cond ((eq? (cdr keyl) '())
(list* (car kvl)
nv
(rec (cddr kvl) keyl nv (+ n 1))))
(else
(list* (car kvl)
(rec (cadr kvl) (cdr keyl) nv '0)
(rec (cddr kvl) keyl nv (+ n 1))))))
(else
(list* (car kvl) (cadr kvl) (rec (cddr kvl) keyl nv n)))))
(rec kvList keyList newValue 0))
(define put-kv ptlPutKvListValue)
#これで無事、動きました。
#それはよかった。
#齊藤さん、ご指摘ありがとうございます。でも確かにalistのアドバンテージも捨てがたいんですよね。どうしようかな。悩ましいなあ・・・
#にちゃんねるでプログラミングの質問に「××するにはどの関数を使えばいいですか」みたいな質問で、めちゃめちゃ限定的な機能を挙げる奴がいるのにぐんにゃりすることがあります。 プログラミングをライブラリを呼び出すことだと思っているんですね。 あまり恵まれた環境だとそういうこともあるのかなぁと。 こういう愚痴は「今時の若いもんは…」の類なので自重しようとは思いつつも割り切れない思いが残ります。 全体的には「いい時代」だとは思うんですけど。
#表現力は同じだと思うのであとは好みでいいと思いますよ。
#いずれにしても match 使えばちょっとスッキリする気がする。 (気がしただけなので気のせいかもしれない)
#他にどんな選択肢があるのかはとても興味あります。Gauche始めてまだ2年だし、継続はまだ理解できた気がしないし、コーディングの盲点もたくさんあるような気がします。ツッコミどころの多いコードを投げることに関してはちょっと気が引ける点はあるのですが、お許しいただけると嬉しいです。でも「ああ、そういうふうに考えればいいのか」と気づかされるのは、とても楽しいです。
#わかってる人にはどこがハマりどころかわかんないので、失敗ケースを書くのも有益ですよ。 それはそれで貢献です。
#ありがとうございます。
#20 年前は脳内ディスプレイが大きかったので、いくらでも長い関数がかけました(笑)。あ、BASIC しか知らなかったから関数とかがそもそもなかったかも。