星田さんの記事に対するコメント。

ちょっとした指の運動にプログラミングの基礎の演習問題好きなのでコツコツ。最大公約数を出す関数・・へぇ!余りを使って出す方法とかあんのか(無知)。寄り道でユークリッド互除法の解説を読んだりする。
ついでに、R7RSではgcdが組み込み関数になってる、つまり、Schemeはgcdを持ってる、って事を確認しておこう。

Listに変換する必要が無いと。ボンヤリとでも存在を知らないと思いつく可能性はゼロなのでやっぱり通読するかなぁドキュメント。
仕様上、string-ci=?の存在は把握しておかなきゃなんないけど、別にリストに変換して悪い、ってワケでもない。
っつーか古典的な意味でのLispだとそもそも文字列が存在せんかったんで(笑)、星田さんの方法論はクラシカルな性交正攻法です。
だから悪くないです。
>> Gaucheで
こっそりここに解答を置いておこう(笑)。

まず前提。
素のSchemeだとSRFI-1の力を借りないと面倒臭い局面になる。
Racketは組み込み関数が多いが、場合によってはやっぱりSRFI-1の力を借りざるを得ない。
一方、Gaucheの場合、本当に必要になったらANSI Common Lispを真似た独自実装を行う場合もあるけど、原則、スタンダード足り得る何かがある、としたらそれに従って、「標準的実装を目指す」と言うような開発ポリシーが感じられる。
要は「奇抜な事はしない」。
結果、Gauche組み込みになってるfoldなんかはSRFI-1のfoldであり、Racketのfoldlより使い勝手がいい。しかもRacketみたいにSRFI-1をわざわざ呼び出す必要はない。まさしく「ビルトイン」されている。
んで、rangeを実装しよう、ってのは練習としてはいいけど、SRFI-1にはiotaって関数がある(※1)。つまり、Gaucheではiotaが使い放題だ。
しかも、RacketのrangeはPythonのrangeに合わせたような動作をするが、SRFI-1のiotaはもっと強力だ。
> (range 1 5)
'(1 2 3 4)
> (iota 5 1)
'(1 2 3 4 5)
>
両者共始点を指定できるが、rangeはあくまでendまでのリストを返すが、iotaの場合は第二引数は生成するリストの長さ、となる。
と言う事はiotaを使えばadd1は必要ない、って事だ。
> (range 1 (add1 (apply min (map string-length '("appletter" "rock")))))
'(1 2 3 4)
> (iota (apply min (map string-length '("appletter" "rock"))) 1)
'(1 2 3 4)
>
明らかにiotaの方がシンプルに書ける。
次はsubstring。
Racketのsubstringは部分文字列の終端を省略可能で、省略した場合は自動に与えられた文字列の「長さ」になる。
と言う事は、Racketのsubstringで省略された第二引数は(string-length 文字列)だ。
> (substring "appletter" 4)
"etter"
> (substring "appletter" 4 (string-length "appletter"))
"etter"
>
R7RSで定義されたsubstringは引数が省略出来ないので、後者のスタイルで書かなければならない、と言う事。
また、問題の性質上、
- 文字列の先頭からi番目までの部分文字列
- 文字列のi番目からケツまでの部分文字列
の2つしか対象にならないんで、結果、やっぱり解は後者しか存在しない。
よって補うべきモノは、与えられた文字列のケツを示すインデックス(string-length)なんだ(※2)。
※2: なお、事実上、RacketのsubstringはむしろSRFI-13のsubstring/sharedに近い。
言い換えると、Gaucheではsubstring/sharedはSRFI-13を特に読み込む事なく使えるんで、末尾指定を避けたい場合はsubstring/sharedを使えばRacketみたいに第二引数を省略出来る。