Clojureの勉強のために、L-99: Ninety-Nine Lisp Problemsを解いてみる。
(map resolve-problem (range 16 21))
といった感じでいってみよう!P16 (**) Drop every N'th element from a list.
(map-indexed vector xs)
でインデックスと要素との組のリストを作り、それを使うことにする。まずは
filter
してmap
するパターン。次に、
(for [... :when ...] ...)
でリスト内包表記を使うパターン。さらに、インデックスと要素の組を作らない解き方として、
partition
でn個ずつのリストを作って、その最後の要素を除いて
concat
していくパターンでもやってみた。注意点としては、
(partition 3 '(a b c d e f g h i k))
では3個ずつの組を作り、3個に満たず余ったもの(この場合はk)は捨てられ
((a b c) (d e f) (g h i))
となってしまうため、余りはnilでパディングするよう
(partition 3 3 nil '(a b c d e f g h i k))
を使うようにした。また、最後の要素を除くので
butlast
が使えそうだったが、((a b c) (d e f) (g h i) (k))
の最後の(k)に対してbutlastするとkが除かれてしまうため、
(take (dec n) %2)
と要素数を決め打ちで取り出すことにした。別解としては、後述するP20のように
reduce
の中でインデックスを作りつつ処理していくやり方でもできそうだ。P17 (*) Split a list into two parts; the length of the first part is given.
「Do not use any predefined predicates.」を述語を使うな、つまりBooleanを返す関数は使うな、と解釈したのだが合っているだろうか…。
split-at
を使えば一発で解けるが、使わないやり方を考えてみた。[nil, xs]から始めて、
iterate
でベクターの右側のリストの先頭を左側に一つずつ移す操作を行い続け、(last (take (inc n)
で指定した位置のベクターを取り出し、左側のリストは逆順に
cons
し続けているのでreverse
する。うーむ、我ながらなかなかトリッキーなコードになってしまった…。
P18 (**) Extract a slice from a list.
インデックスが分かれば解き方は色々ありそうなので、P16やP20のようないくつかの解法がありそうだ。今回はリスト内包表記で。P19 (**) Rotate a list N places to the left.
今回はsplit-at
を使った。負数の場合、インデックスを調整しないといけないのでpos?
で判定している。P20 (*) Remove the K'th element from a list.
こちらもインデックスが分かればどうとでもできるのでP16とほぼ同じやり方でもできそうだったが、あえて別のやり方で。[nil, 0]から始めて、
reduce
の中でリストを作りつつ除外する位置かどうかのインデックスも作りつつ畳み込んで、畳み込んだ結果のリストとインデックスの組から、リストだけ取り出して逆順にする。