星田さんの記事に対するコメント。
こうなってたけど順番が謎だぞ?と
うん。
「キーワード引数」ってのは順序は関係ない、んです。
Pythonでもそうなんで覚えていた方がエエかも。
元々Schemeには仕様上、オプショナル引数もなかった。
確か元々「オプショナル引数」が導入されたのはR6RSかな?case-lambdaの導入でオプショナル引数が仕様に含まれた。
だから、オプショナル引数は新参者なんだけど、いまだキーワード引数ってのは正式仕様じゃない。
もちろんRacketは既にSchemeを止めて久しいんだけど、結局、キーワード引数はRacketの独自拡張で、また、ぶっちゃけ、ANSI Common Lispからその機能を持ってきてる。
通常、引数ってのは順番通りに実引数が手渡される。よって「順序引数」と呼ばれる事もある。オプショナル引数でさえ「順序」が大事だ。
一方、キーワード引数は順序は関係ない。オプショナル引数じゃない「必須の引数」の後だったらどこに置いても良い、ってパターンが多い。
例えば次のような単純な関数を書いてみる。
(define (keylist a #:key (x '()) (y '()) (z '()))
`(,a ,x ,y ,z))
順序としてはキーワード引数であるxはデフォルト値としては'()が与えられ、その位置は2番目だ。
しかし、実行する際にキーワード引数xに与える実値はどこに置いても問題がない、ってのが次の実行例で分かると思う。
> (keylist 1) ; 必須引数aに1を与えたパターン
'(1 () () ())
> (keylist 1 #:key 2) ; 必須引数aに1を、キーワード引数xに2を与えたパターン
'(1 2 () ())
> (keylist 1 2) ; キーワード引数を使わないので2番目の値は順序によりyだと解釈される
'(1 () 2 ())
> (keylist 1 3 #:key 2) ; 実行時にキーワード引数へ2を与える位置を最後にしても関数定義時の「位置」に2が入る
'(1 2 3 ())
>
つまり、キーワード引数を使った場合、sortの実行は次のどれでも正解、って事になるわけだ。
順序引数は順序が大事だけど、キーワード引数はどこに挿入して使っても構わない。
キーワード引数の重要な性質なんで押さえておこう。
昼間諦めていたドットリストで座標を備えた構造体のリストのSort問題、こうやって書けば・・
ちゃんと出来た、スゲーッ!
あと、こういう手もあります。
つまり、こう継承してるわけでしょ?
(struct CHARACTER (Name Race Class Ali Lv Hp Ac Exp Money Move foo Arm Armor Item Str Int Wis Dex Con Chr) #:transparent)
(struct HERO () #:super struct:CHARACTER)
(struct ENEMY () #:super struct:CHARACTER)
そして、これで作ったインスタンスをそれぞれ「位置情報」にconsしたリストを作る、と。
`(
(,(HERO "tawa" "ELF" "MAGIC-USER" "" 1 6 10 0 90 6 '() '() '() '() 10 18 6 11 9 10) . ,(make-posn 31 31))
(,(HERO "hosida" "HUMAN" "FIGHTER" "" 1 1 10 0 90 5 '() '() '() '() 17 10 12 8 15 14) . ,(make-posn 93 93))
(,(ENEMY "DEMON1" "ENEMY" "" "" 1 1 10 0 90 5 '() '() '() '() 10 10 10 18 10 10) . ,(make-posn 155 155))
(,(ENEMY "DEMON2" "ENEMY" "" "" 1 1 10 0 90 5 '() '() '() '() 10 10 10 2 10 10) . ,(make-posn 217 217)))
これを構造体(CHARACTER)のDEX値でsortしたい場合は次のように書けばいい。
(sort `(
(,(HERO "tawa" "ELF" "MAGIC-USER" "" 1 6 10 0 90 6 '() '() '() '() 10 18 6 11 9 10) . ,(make-posn 31 31))
(,(HERO "hosida" "HUMAN" "FIGHTER" "" 1 1 10 0 90 5 '() '() '() '() 17 10 12 8 15 14) . ,(make-posn 93 93))
(,(ENEMY "DEMON1" "ENEMY" "" "" 1 1 10 0 90 5 '() '() '() '() 10 10 10 18 10 10) . ,(make-posn 155 155))
(,(ENEMY "DEMON2" "ENEMY" "" "" 1 1 10 0 90 5 '() '() '() '() 10 10 10 2 10 10) . ,(make-posn 217 217)))
> #:key (match-lambda ((cons head tail) ; 注目!
(CHARACTER-Dex head))))
lambdaが使えるトコは当然match-lambdaも使える、と言う事に注目しよう。
こういう時に、散々っぱら鍛えた「パターンマッチング」を堂々と応用しよう。