Schemeで二重のループってどうやるの?

あるリストについて二重のループを実行したい。
例えば Python で書くとこういうこと。

def loop_double(lis):
    for i in lis:
        for j in lis:
            print [i, j]

loop_double([1, 2, 3])
takatoh@nightschool $ python loop_double.py
[1, 1]
[1, 2]
[1, 3]
[2, 1]
[2, 2]
[2, 3]
[3, 1]
[3, 2]
[3, 3]

すごく簡単そうに見えるけど、Scheme でやってみたらどうやるのかわからなくて悩んだ。ググっても Scheme の二重ループの例は見当たらない。
で、結局こう書いた。

(define loop-double
  (lambda (lis)
    (letrec ((f (lambda (l1 l2)
      (if (null? l1)
          '()
          (append (map (lambda (x)
                    (cons (car l1) (list x))) l2)
            (f (cdr l1) l2))))))
            (f lis lis))))

(for-each print (loop-double '(1 2 3)))

Scheme らしく、ループの中で処理を実行するんじゃなくて引数のリストを返す手続きにしてみた。それはいいんだけど、内側では map を使ってるんで厳密には二重ループじゃないよな。

takatoh@nightschool $ gosh loop-double.scm
(1 1)
(1 2)
(1 3)
(2 1)
(2 2)
(2 3)
(3 1)
(3 2)
(3 3)

まあ、ほしい結果は得られたんでとりあえずは良しとするか。
でもホントはどうやったらいいんだろう?

「Schemeで二重のループってどうやるの?」への1件のフィードバック

  1. その場合はこんな感じで表現できますね。

    (define (loop-double lis)
    (append-map (lambda(x) (map (lambda(y) (list x y)) lis)) lis))

    ※ append-map は map した結果を append するだけのものですが、中間的なリストを構築しないので少し効率がいいです。

    もっとループっぽい書き方が良いなら do を使うのが楽だと思います。

    (define (loop-double lis)
    (let ((result ‘()))
    (do ((e1 lis (cdr e1)))
    ((null? e1))
    (do ((e2 lis (cdr e2)))
    ((null? e2))
    (set! result (cons (list (car e1) (car e2)) result))))
    (reverse result)))

    そして srfi-42 が使える状況であれば (Gauche なら (use srfi-42) すれば) こんなすっきりした書き方も出来ます。

    (define (loop-double lis)
    (list-ec (: x lis) (: y lis) (list x y)))

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください