私はPython3の機能容量で遊んでおり、ハミング数を計算するための古典的なアルゴリズムを実装しようとしました。これが素因数として2、3、または5しかない数です。最初のハミング数は2、3、4、5、6、8、10、12、15、16、18、20などです。
私の実装は次のとおりです。
def scale(s, m):
return (x*m for x in s)
def merge(s1, s2):
it1, it2 = iter(s1), iter(s2)
x1, x2 = next(it1), next(it2)
if x1 < x2:
x = x1
it = iter(merge(it1, s2))
elif x1 > x2:
x = x2
it = iter(merge(s1, it2))
else:
x = x1
it = iter(merge(it1, it2))
yield x
while True: yield next(it)
def integers():
n = 0
while True:
n += 1
yield n
m2 = scale(integers(), 2)
m3 = scale(integers(), 3)
m5 = scale(integers(), 5)
m23 = merge(m2, m3)
hamming_numbers = merge(m23, m5)
マージが機能しないように見えるという問題。その前に、私はエラトステネスのふるいを同じ方法で実装しました、そしてそれは完全にうまくいきました:
def sieve(s):
it = iter(s)
x = next(it)
yield x
it = iter(sieve(filter(lambda y: x % y, it)))
while True: yield next(it)
これは私のマージ操作と同じテクニックを使用しています。ですから違いはわかりません。あなたはなにか考えはありますか?
(これらはすべて他の方法で実装できることを知っていますが、クラス宣言や特別なビルド済みPython関数を使用せずに、Pythonのジェネレーターと再帰を含む純粋な関数型機能を正確に理解することを目標としています。)
UPD:Will Nessの場合、LISP(実際にはRacket)でのこのアルゴリズムの実装は次のとおりです。
(define (scale str m)
(stream-map (lambda (x) (* x m)) str))
(define (integers-from n)
(stream-cons n
(integers-from (+ n 1))))
(define (merge s1 s2)
(let ((x1 (stream-first s1))
(x2 (stream-first s2)))
(cond ((< x1 x2)
(stream-cons x1 (merge (stream-rest s1) s2)))
((> x1 x2)
(stream-cons x2 (merge s1 (stream-rest s2))))
(else
(stream-cons x1 (merge (stream-rest s1) (stream-rest s2)))))))
(define integers (integers-from 1))
(define hamming-numbers
(stream-cons 1 (merge (scale hamming-numbers 2)
(merge (scale hamming-numbers 3)
(scale hamming-numbers 5)))))