計算機プログラムの構造と解釈 第二版 P65 問題2.32

こんな問題は簡単だ。(と思っていたら難しかった。)
受験勉強的には。


要は、穴埋め問題なんだけど、

(define (subsets s)
(if (null? s)
(list nil)
(let ((rest (subsets (cdr s))))
(append rest (map rest)))))

の<??>って部分を埋めて、

(1 2 3)

って集合から、すべての部分集合の集合

( () (3) (2) (2 3) (1) (1 3) (1 2) (1 2 3))

が出るようにすればいい。


ふんぬ。わらわせるぜ。(受験勉強的には)


ここでドラゴン桜的には、
・の中には何か手続き(lambda)がはいる。
・(cdr s)っていうのがあるから、たぶん、(car s)はここで使うだろう。
・たぶん、consかlistかappendでリストをつなげるような関数になるだろう。


って言う予測は何となく立てられる。
そんでまーためしてみる。まずはconsで試してみましょう。
の中にこんな感じのをいれてみる。

(lambda (x) (cons (car s) x))

そしたら、一発でいけた。


でも、こういうやり方でやると、実際、人気者になれない。
おかげで僕は(間借りさせてもらっている)社内でつまはじきものにされ、発言権を失い。
窓際に追いやられ、現在では雀の数を数えるだけの毎日だ。
まさに世紀末。


まーいい。やり方はいろいろある。
僕が勤めてる会社(が間借りさせてもらっている会社)には、
異常に頭がいい人がいる。(id:ajiyoshi)
彼は猫好きで、僕は猫アレルギーだが特に問題は生じない。


ということで彼の解説を僕流の咀嚼で書いてみると。

( () (3) (2) (2 3) (1) (1 3) (1 2) (1 2 3))

上のリストから3が入っている部分だけくりぬいてみる。
そうすると

A
 (() (2) (1) (1 2))

B
 ((3) (2 3) (1 3) (1 2 3))

すると何か法則性があるよね。
Aの各要素に、3をconsしてやった結果が、Bの各要素になってる。
そして、AとBをappendしてやった集合が、求める答えだ。


これでもまだ納得しない小僧は次にAを2のはいっている部分だけでくりぬいてみるといい。

A1
 (() (1))

A2
 ((2) (1 2))

A1の各要素に、2をconsしてやった結果が、A2の各要素になってる。
そして、A1とA2をappendしてやった集合が、求める答えだ。


まるでデジャブを見ているように繰り替えされているこの構造!!


実装

#!/usr/local/bin/gosh
;; -*- coding: utf-8 -*-
(define nil '())

(define myaggregate (list 1 2 3)) 

(define (func x)
  (display x)) 

(define (subsets s)
  (if (null? s)
    (list nil)
    (let ((rest (subsets (cdr s))))
      (append rest (map (lambda (x) (cons (car s) x)) rest)))))


;; main
(define (main args)

  (display "myaggregate: ")
  (display myaggregate)
  (newline)

  (display "(subsets myaggregate): ")
  (display (subsets myaggregate))
  (newline)

  0)  


実行

myaggregate: (1 2 3)
(subsets myaggregate): (() (3) (2) (2 3) (1) (1 3) (1 2) (1 2 3))

できた!!