練習:名前つきlet

名前つきlet の練習。

 cf. 7. 繰り返し – もうひとつの Scheme 入門

の練習問題3より。

my-delete

リスト (ls) から要素 (x) を取り除いたリストを返す関数。

(define my-delete
  (lambda (x ls)
    (let loop ((y x) (l1 ls) (l2 '()))
      (if (null? l1)
          (reverse l2)
          (loop y (cdr l1) (if (eq? y (car l1))
                               l2
                               (cons (car l1) l2)))))))

(print (my-delete 'c '(a b c d e)))
^o^ > gosh my-delete3.scm
(a b d e)

この関数は下のように素直な再帰のほうがわかりやすいけど、代わりに末尾再帰にならない。

(define my-delete
  (lambda (x ls)
    (cond
      ((null? ls) '())
      ((eq? x (car ls)) (my-delete x (cdr ls)))
      (else (cons (car ls) (my-delete x (cdr ls)))))))

index

リスト (ls) の要素 (x) の位置を返す関数。位置は 0 から数え始め、 x がない場合は #f を返す。

(define index
  (lambda (x ls)
    (let loop ((y x) (l ls) (n 0))
      (cond
        ((null? l) #f)
        ((eq? y (car l)) n)
        (else (loop y (cdr l) (+ n 1)))))))

(print (index 'c '(a b c d e)))
(print (index 'f '(a b c d e)))
^o^ > gosh index2.scm
2
#f

sum

数のリストの要素の合計を求める関数。

(define sum
  (lambda (lis)
    (let loop ((l lis) (s 0))
      (if (null? l)
          s
          (loop (cdr l) (+ s (car l)))))))

(print (sum '(1 2 3 4 5)))
^o^ > gosh sum.scm
15

string->integer

正の整数を表す文字列を整数に変関する関数。入力エラーチェックはしなくて良い。
例: “1232” → 1232
ヒント:

  1. 文字 #\0 … #\9 のASCII コード番号から 48 を引くとその数そのものになります。 アスキーコードを求める関数は char->integer です。
  2. 文字列を文字のリストに変換する関数 string->list を使うと便利です。
(define string->integer
  (lambda (str)
    (let loop ((ls (string->list str)) (i 0))
      (if (null? ls)
          i
          (loop (cdr ls) (+ (* i 10) (- (char->integer (car ls)) 48)))))))

(print (string->integer "1234"))
^o^ > gosh string-to-integer.scm
1234

range

0 から n 未満の整数のリストを返す関数。

(define range
  (lambda (n)
    (let loop ((m (- n 1)) (l '()))
      (if (< m 0)
          l
          (loop (- m 1) (cons m l))))))

(print (range 10))
^o^ > gosh range.scm
(0 1 2 3 4 5 6 7 8 9)

ave

任意個の引数をとりそれらの平均を返す関数。レストパラメータを使う。全ての引数は数、エラー処理は不要。

(define ave
  (lambda ls
    (let loop ((l ls) (s 0) (n (length ls)))
      (if (null? l)
          (/ s n)
          (loop (cdr l) (+ s (car l)) n)))))

(print (ave 1.1 2.3 4.6))
(print (ave 3.3 4.7 10.2 20.6 100.1))
^o^ > gosh ave.scm
2.6666666666666665
27.779999999999994

(lambda ls ...) という書き方で、0個以上の引数をとりすべて ls にリストとして束縛される。以下も参照。
 cf. 可変長の引数をとる関数