龍虎氏の記事(2024/02/13と2024/02/14)に対してのコメント。
ふふふふ(笑)。面白い事をやってる(笑)。
しかし、スタックオーバーフローの連中も面食らっただろうなぁ(しかも、相手は「Lisper」だから無下にも出来ないし、ある意味楽しんでたんじゃないか・笑)。
でも、いっつも言ってる通り、「ある重要機能を使いこなす」には、ギリギリを攻めるのが一番なんだ。
それが「いつそれを使えて、いつそれが使えないんだ」と言う「見切り」を育てる。
だから練習段階ではアレコレ考えて「無理をしてみる」べきだ。
んで、確かに回答者の一人が言ってる通り、
「元となる数値が次第に減っていき, 除数より小さくなれば終了」
というパターンは (for 〜 in sequence の処理には) 向かない
っつーのはその通りなんだ。単項の数値がどうの、と言うとリスト内包表記での処理が難しくなる。
実は仮に「連続してある数値を割ってリストを生成する」rangeがあれば、この問題は一挙解決だ。そうすれば「単項からリストが生成出来る」・・・・・・どっかで聞いた事がない(笑)?
そう、実はこれ、最近、僕のブログの方で扱ってる余再帰向けの問題、なの。
苦しくも、回答者が、
ジェネレーターが必要になりそう
って言ってるのはその通り、なんだ。
問題はその「ジェネレータ」をこの問題に特化したカタチで書くのか、あるいは汎用性があるように作るべきか、ってので方針が違う。そして余再帰関数unfoldは後者の方針なんだ。
実際、この問題、SchemeだとSRFI-1のunfoldで一行で書けてしまう。
> (reverse (unfold zero? (lambda (x) (modulo x 2)) (lambda (x) (quotient x 2)) 499))
'(1 1 1 1 1 0 0 1 1)
このリストが正しいか否かは次のようにして確認出来る。
> (number->string 499 2)
"111110011"
正しいよね。
つまり、Pythonで数学をやって、このテのパターンが今後も出てくるとしたら、余再帰関数を自作して、ライブラリ化しておいてもいいと思う。Pythonに足りないのは余再帰だ、って今ここでハッキリしたわけだから。
もう一度、余再帰関数unfoldrの定義を置いておく・・・Pythonだとunreduceの方がいいのかもしれんが(笑)。
### 余再帰
class unfoldr:
def __init__(self, f, seed):
self.f = f
self.seed = seed
def __iter__(self):
return self
def __next__(self):
match self.f(self.seed):
case a, b:
self.seed = b
return a
case None:
raise StopIteration
def __reversed__(self): # reversed を簡易に行う為の特殊メソッド
return reversed(list(self))
そしてPythonだと
- リストに何かをマッピングしてリストを得る: リスト -> リストならリスト内包表記
- リストを何らかの単項データへ畳み込む: リスト -> 何かのデータならfunctools.reduce
- 単項から何らかの規則に従ってリストを生成する: 何らかのデータ -> リストならunfoldr
と言うのが見えるんじゃないか。
3種の神器(マジでだ・笑)を揃えれば、Pythonでも畏れるモノがない、ってこったな(笑)。
なお、上のunfoldrの定義(Haskellスタイル)だと、与題は、
>>> list(reversed(unfoldr(lambda x: None if x == 0 else (x % 2, x // 2), 499)))
[1, 1, 1, 1, 1, 0, 0, 1, 1]
と簡単に一行で書けます。