リスト処理の小技を書き留める。
ひげぽんさんのところより:
整列済みの number のリストがある。
'(1 3 4 5 6 12 13 15)このようなリストで数が連続している部分は '(1 2 3) -> '(1 . 3) のように両端のみを書くような記法を導入する。
最初の例のリストであれば以下のようになる。
'(1 (3 . 6) (12 . 13) 15)このようなリストの変換をするコードを書きたい。
ひげぽんさんの実装は上記エントリを参照。
Shiro (2008/09/25 03:41:37 PDT): 最初、コメント欄でこういう回答を寄せた。 わりと何も考えずに書き下した感じ。
(define (compact-number-list lis) (define (scan xs prev head r) (cond [(null? xs) (reverse (push prev head r))] [(not prev) (scan (cdr xs) (car xs) (car xs) r)] [(= (car xs) (+ prev 1)) (scan (cdr xs) (car xs) head r)] [else (scan (cdr xs) (car xs) (car xs) (push prev head r))])) (define (push prev head r) (if (= prev head) `(,head ,@r) `((,head . ,prev) ,@r))) (scan lis #f #f '()))
そしたら、Scalaで実にエレガントな解法が
case class R(s: Int,e: Int) case class N(n: Int) extends R(n,n) def f( input: List[R] ): List[R] = input match { case R(c1,c2)::N(c3)::cdr if(c2+1==c3) => f(R(c1,c3)::cdr) case car::cdr => car::f(cdr) case r => r } println(f((1::3::4::5::6::12::13::15::Nil)map(N(_)))) => List(N(1), R(3,6), R(12,13), N(15))
マッチ節にガードを使ってるとこと、R(s,e)のサブクラスでN(n)が定義できちゃうところが かっこいいのか。前者はutil.matchを使えばできなくはない。
(use util.match) (define (compact-number-list lis) (define scan (match-lambda [((a . b) (c . _) . xs) (=> next) (if (= (+ b 1) c) (scan `((,a . ,c) ,@xs)) (next))] [(p . xs) `(,p ,@(scan xs))] [xs xs])) (map (match-lambda [(x . y) (if (= x y) x `(,x . ,y))]) (scan (map (lambda (x) (cons x x)) lis))))
ただ、入力数列をペアのリストへ変換し、そして結果をまた変換する分がちょっとまどろっこしい。 (Scalaの回答も最後に結果を戻してないんだけど、heterogeneousなリストは 扱いにくいだろうからScala的にはあれで良いのだろう)
(use util.match) (define (compact-number-list xxs) (define (f x y) (match y (((a . b) . c) (if (= (+ b 1) x) (cons (cons a x) c) (cons x y))) ((a . c) (if (= (+ a 1) x) (cons (cons a x) c) (cons x y))))) (match xxs (() ()) ((x . xs) (reverse (fold f (list x) xs)))))
1つの要素をもつリストかどうかを調べる。
(define (single? l) (and (pair? l) (null? (cdr l))))
補記:
(= (length l) 1)
こうして調べるのはとても非効率的だ.第1要素を見終わった直後にはもう必要な情報は分かっているからだ.
整数のリストをもらって、おのおのの要素にその位置を示す数を加えてかえす -- Ansi Common Lisp Ex 3.5
gauche.sequenceを使うと楽ができる。
(use gauche.sequence) (define (pos+ seq) (map-with-index + seq))
これじゃ課題にならないか。素直な実装:
(define (pos+ lis) (let loop ((r '()) (cnt 0) (lis lis)) (if (null? lis) (reverse! r) (loop (cons (+ cnt (car lis)) r) (+ cnt 1) (cdr lis)))))
map-accum というのもある。
(use gauche.collection) (define (pos+ xs) (map-accum (lambda (x i) (values (+ x i) (+ i 1))) 0 xs))
(Scheme:Treeに移動しました。)
(子)リストのリストがあって、子リストにはシンボルが2個以上入ってたとする。 たとえば、((A B) (C D) (E F) (A G) (H F I)) のような感じ。
これを、同じシンボルを含む子リストはまとめたいとする。 たとえば、例で言えば ((A B G) (C D) (E F H I)) のようなリストを返す。
Shiroの解答。あんまり効率は考えてない。(2003/08/21 00:41:40 PDT)
(use srfi-1) (define (solve sets) (define (merge1 lst root) (receive (diff intersection) (lset-diff+intersection eq? lst root) (and (not (null? intersection)) (append root diff)))) (define (merge2 lst roots) (if (null? roots) (list lst) (let* ((root (car roots)) (newroot (merge1 lst root))) (if newroot (cons newroot (cdr roots)) (cons root (merge2 lst (cdr roots))))))) (fold merge2 '() sets))
Y.Hana (2003/08/21 09:16:44 PDT) ↓遅そうな気もしますが。
(use srfi-1) (define (solve xs) (fold (lambda (x xs) (receive (p q) (partition (pa$ member x) xs) (cons (apply lset-union eq? p) q))) xs (concatenate xs)))
(define (solve x) (define (f x y) (let loop ((z (cdr y)) (r '())) (cond ((null? z) (cons (cons x (car y)) r)) ((member x (car z)) (cons (append (car z) (car y)) (append (cdr z) r))) (else (loop (cdr z) (cons (car z) r)))))) (fold (lambda (x y) (fold f (cons '() y) x)) '() x))
data = (a-value b-value c-value d-value)
みたいに表現されたデータが渡された時、変数a-var, b-var, c-var, d-varに それぞれa-value, b-value, c-value, d-valueの値を入れたい。
(receive (a-var b-var c-var d-var) (apply values data) 処理 ...)
lambdaを気にしないのであれば
(apply (lambda (a-var b-var c-var d-var) 処理 ...) data)
でも良いが、receiveの方が見やすいと思う。他にもbindしたい変数があるときは let-valuesとか使えばいいし。
Biglooにはさらに複雑な構造を分解できるパターンマッチャがついてきたはず。
2002/05/18 02:16:22 PDT
現在は Gauche にも util.match がある。
(use util.match) (match-let ([(a-var b-var c-var d-var) data]) 処理 ...)
(Scheme:Puzzleに移動しました。)
技という程のこともないが、非常に良く使うので書き留めておく。 場所fooに要素のリストが保持されているとする。
追加 (push! foo item) 削除 (update! foo (cut delete item <> eq?))
deleteはsrfi-1、cutはsrfi-26。push!とupdate!はGauche特有のマクロだが、 さほど手間無く書ける。srfi-17(generalized set!) 対応であればfooのところには 一般化された式が書ける。
(push! (cdr *data*) item) (push! (slot-ref object 'item-list) item) 等々...
(2002/10/24 21:56:25 PDT)
リストの各要素への繰り返しはfor-eachやmapで済むけど、 時々ちょっと変わった繰り返しが必要になることがある。
リスト(a b c d)が与えられた時、 (a b c d), (b c d), (c d), (d)のように繰り返す。 CommonLispではmaplと呼ばれてる処理。
(use srfi-1) (pair-for-each proc '(a b c d))
リスト(a b c d)が与えられた時、(a), (a b), (a b c), (a b c d)のように繰り返す。 手続き的に考えたらインデックスiを0からリストの長さまで回して 順次最初のi要素を取るんだろうけど、あんまりらしくない。
(define (reverse-mapl proc lis) (define (rec rlis) (if (null? (cdr rlis)) (proc rlis) (begin (rec (cdr rlis)) (proc (reverse rlis))))) (unless (null? lis) (rec (reverse lis))))
こんな感じかな。あるいはsrfi-1のpair-fold-rightを使って
(pair-fold-right (lambda (l knil) (proc (reverse l))) '() (reverse lis))
なお、副作用ではなくmapのように結果のリストが欲しい場合は わざわざ実行順序を逆にしなくても、上の第2段階の繰り返しにしといて 結果を逆順にconsしてやれば良い。末尾再帰でやれば自然な順序が 逆順になるから簡単だ。
(pair-fold (lambda (l knil) (cons (proc (reverse l)) knil)) '() (reverse lis))
結果順ではなく作用順が必要なケースとしては、与えられたパスのディレクトリを 作成する処理などがある。根から順にディレクトリの存在を調べて無かったら 作っていかねばならない。
(2002/12/31 20:06:32 PST)
(define (inits xxs) (if (null? xxs) ('()) (let ((x (car xxs)) (xs (cdr xxs))) (cons '() (map (lambda (ls) (cons x ls)) (inits xs)))))) (define (tails xxs) (if (null? xxs) ('()) (cons xxs (tails (cdr xxs)))))(2003/01/02 07:46:34 PST)
(Scheme:Treeに移動しました。)
(Scheme:Treeに移動しました。)