1 つの関数に注目してみましょうshow-deck
。何をしているのかはわかりますが、再帰は少し単純化できます。読みやすくすることは少し効率が悪いかもしれませんが、ここでは 52 枚のカードについて話しています... :)
元のコードでは内側と外側のループが絡み合っています。それらを解きほぐす1つのバージョンを次に示します。
(define (show-deck1 first-list second-list)
(define (outer-loop first-list second-list)
(cond
((null? first-list)
'())
(else
(append (inner-loop (car first-list) second-list)
(outer-loop (cdr first-list) second-list)))))
(define (inner-loop x second-list)
(cond
((null? second-list)
'())
(else
(cons (cons x (car second-list))
(inner-loop x (cdr second-list))))))
(outer-loop first-list second-list))
簡略化を適用できます。定義をこのように表現すると、 をmap
使用してinner-loop
.
(define (show-deck2 first-list second-list)
(cond
((null? first-list)
'())
(else
(define x (car first-list))
(append (map (lambda (y) (cons x y)) second-list)
(show-deck2 (cdr first-list) second-list)))))
これにより、外側の反復の構造が見やすくなります。さらに一歩map
進んで、内側と外側の両方のループに使用(apply append ...)
し、 のネストされた使用によって導入されたサブ構造を平坦化するために使用できますmap
。
(define (show-deck3 first-list second-list)
(apply append
(map (lambda (x)
(map (lambda (y) (cons x y)) second-list))
first-list)))
(apply append ...)
あなたのバージョンでは、計算を巧みにスレッド化することでこの問題を完全に回避していますが、読みやすさが犠牲になっています。を回避し(apply append ...)
、ネストされたループ構造を簡単に確認できる利点を得る1 つの方法は、foldr
アプローチではなくアプローチを使用することmap
です。
(define (foldr f acc l)
(cond
((null? l) acc)
(else
(f (car l)
(foldr f acc (cdr l))))))
(define (show-deck first-list second-list)
(foldr (lambda (x acc)
(foldr (lambda (y acc)
(cons (cons x y) acc))
acc
second-list))
'()
first-list))
これは、元のコードが以前に行ったものと一致する必要があります。ただし、ループ性を に委譲するfoldr
ため、これらの関数はすべて のiter
使用に消えますfoldr
。
個人的には、完全な Racket で書いている場合は、for
ループでコーディングするだけです。:) 次のようになります。
;; If we're allowed to use full Racket:
(define (show-deck first-list second-list)
(for*/list ([x first-list]
[y second-list])
(cons x y)))