(define -ayalog '())

括弧に魅せられて道を外した名前のないプログラマ

named-letの練習

なんかnamed-letが分かりにくかったので書いて理解しようとした。

文字列の空白を除いて文字数をカウントする手続き

(define (str-count str)
  (let loop ((ls (string->list str))
	     (count 0))
    (let ((inc (lambda (x) (+ x 1))))
      (cond
       ((null? ls) count)
       ((char-whitespace? (car ls)) (loop (cdr ls) count))
       (else
	(loop (cdr ls) (inc count)))))))

;;string-lengthだと17だけど…
(str-count "ayato_p & ayato_p") ;;=> 15

named-let使うとlambdaで末尾再帰書かなくてもいいよってのが分かればいいのかな。
個人的には、取っ付きにくかったけど、わかってしまえばなんてことないので好きです。
なんとなーく使い方は分かった!!(たぶん)

あと、数のリスト*1から10進数の数字をつくる手続きも書いてみた。

数のリストから10進数の数をつくるよ!!

(define (comp-tup tup)
  (define (pow x y)
    (let iter ((a x) (b y) (c 1))
      (if (zero? b)
	  c
	  (iter a (- b 1) (* c a)))))
  (let tup-iter ((t tup)
		 (comp 0))
    (cond
     ((null? t) comp)
     (else
      (let ((size (- (length t) 1)))
	(let ((exp (pow 10 size))
	      (x (car t)))
	  (tup-iter (cdr t) (+ comp (* x exp)))))))))

(comp-tup '(1 2 3 4)) ;;=> 1234
(comp-tup '(1 0 0 0)) ;;=> 1000
(comp-tup '(0 0 0 1)) ;;=> 1

と、ここまで書いた段階でvallogの中の人からツッコミ入った。

140文字以内に収まるとか…。。。

余談1

こんな感じで再帰書こうとしたら、ダメだった…。
letに書ける手続きって、再帰しないlambdaだけなのかなー。とか。

;;前略
      (let ((size (- (length tup) 1))
	    (pow (lambda (a b)
		   (if (zero? b)
		       1
		       (* a (pow a (- b 1)))))))
;;後略
追記

letrecで書けた…。named-letで書ける気がするけども、そっちは良くわからない。*2

(define (comp-tup tup)
  (define (comp-tup-iter tup comp)
    (cond
     ((null? tup) comp)
     (else
      (let ((size (- (length tup) 1)))
	(letrec ((pow (lambda (a b)
		   (if (zero? b)
		       1
		       (* a (pow a (- b 1)))))))
	  (let ((exp (pow 10 size))
		(x (car tup)))
	    (comp-tup-iter (cdr tup) (+ comp (* x exp)))))))))
  (comp-tup-iter tup 0))

余談2

もともとこの手続き2つを書こうと思ったきっかけは、ProjectEulerのProblem11で、こういうデータを取り扱わないといけなかったので、なんかこれを解くための補助手続き書かないと駄目だよなーって思って書きはじめたんだけど…。

08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08
49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00
81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65

最初、これは文字列で引数として受け取って、分解して云々…ってしようと思ってたんだけど、Scheme(Lisp)って「08」とか書いてあっても10進数の「8」として解釈してくれるんですね…って後で気づきました。

'((08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08)
  (49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00)
  (81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65))

これでいいらしい。。。

最初はこうしてから、どうにかパースしないといけないのかなーとか…。

'("08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08"
  "49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00"
  "81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65")

続き…(2/14追記)

letで再帰する手続き書けないなぁと書いていたら、@さんからvallogの記事に誘導されました。

そういえば前日眺めてたなー…とか。

で、元々named-letについて書いてたんですけど、僕の書いていた記事中の手続きは「こう書けるよ!」的なツイートが@さんと@さんからポンポン投げられてきました。


はい、とても楽しかったです(しろめ
愚直な書き方というか、今まではこういう書き方しか知らなかったというか、まだそんなに詳しくないので勉強しなきゃなとおもいますね。


そして、僕の書いた手続きはsrfi-13にあることが判明(多少抽象度が高いみたいですが、高性能ですね)
string-count SRFI 13:String Libraries


載せました><

そうです。元はnamed letの話でしたw
勉強になりました><

*1:tupleって言うのかな?

*2:named-letで書くと、不必要に読み難くなりそう