自然数の分割
Posted feedbacks - Scheme
Schemeならsrfi-42でcomprehensionが使えます。
1 2 3 4 5 6 7 | (use srfi-42)
(define (partition-num n m)
(if (= m 1)
`((,n))
(list-ec (: x 0 (+ n 1)) (: xs (partition-num x (- m 1)))
(cons (- n x) xs))))
|
n=5 m=3 の場合、
(reverse (list-ec (: a 6) (: b 6) (: c 6) (if (= 5 (+ a b c))) (list a b c)))
というコードが生成されれば良い。
→マクロで書いてみる。
→nとmがコンパイル時に決まらないから無理?
→生成したコードをevalする方針に変更。
とこんな風になりました。
;; comprehensionと再起の組み合わせであんなに短く書けるとは。
1 2 3 4 5 6 7 8 9 10 11 12 13 | (use srfi-1)
(use srfi-42)
(define (quolifiers upper-bound n-quos)
(let1 gensyms (map (lambda (x) (gensym)) (iota n-quos))
(values gensyms
(map (lambda (sym) `(: ,sym ,upper-bound))
gensyms))))
(define (partition-num n m)
(receive (syms form) (quolifiers (+ n 1) m)
(eval `(reverse (list-ec ,@form (if (= ,n (+ ,@syms))) (list ,@syms)))
scheme-report-environment)))
|
リストのネスト具合の辻褄合わせが大変だった。 静的型言語ならコンパイルするだけでチェックできるのに……
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | (use util.match)
(define-constant USAGE
(string-join '("usage: command n m"
" n, m -> positive integer (n >= m > 0)") "\n"))
(define (split-num_ base n m acc)
(cond ((= m 0) `((,n)))
((< n 0) acc)
(else
(let* ((base_ (- base n))
(sub (split-num_ base_ base_ (- m 1) '()))
(acc_ (map (pa$ cons n) sub)))
(split-num_ base (- n 1) m (append acc_ acc))))))
(define (split-num n m)
(cond ((not (and (>= n m) (> m 0))) (print USAGE))
(else
(let* ((nums (split-num_ n n (- m 1) '()))
(lines (map (lambda (lst)
(string-join (map number->string lst) ", " 'suffix))
nums))
(sorted (sort lines (lambda (a b) (compare b a)))))
(string-join sorted "\n")))))
(define (main argv)
(match argv
((_ n m)
(print (split-num (string->number n) (string->number m))))
(_ (print USAGE))))
|




herumi
#4099()
Rating1/1=1.00
[ reply ]