M.Hiroi's Home Page

Memorandum

プログラミングに関する覚え書や四方山話です。
[ Home | 2020年 1月, 4月, 12月 ]

2020 年 12 月

12月31日

●大晦日

今年も残りわずかとなりました。M.Hiroi's Home Page を開設してから 20 年が過ぎましたが、ここまで長く続けることができるとは M.Hiroi も思っていませんでした。これもひとえに M.Hiroi's Home Page に来てくださる皆様のおかげです。本当にありがとうございました。来年の事を言えば鬼が笑うといいますが、これからも M.Hiroi's Home Page の更新を続けることができればいいなあ、と思っております。

それでは、来たるべき新年が皆様にとって素晴らしい年でありますように。


12月28日

●ISLisp で Lisp の初歩から

笹川さんが YouToube で ISLisp で Lisp の初歩から という動画を公開されています。その中で、拙作のページ お気楽 ISLisp プログラミング超入門 で作成したプログラムを例題として取り上げていただきました。ありがとうございます。動画による解説はわかりやすくていいですね。これからも楽しみにしていますのでがんばってください。

笹川さんが開発中の ISLisp 処理系 Easy-ISLisp は以下のページからダウンロードすることができます。

ところで、FizzBuzz 問題は 1 から 100 までの数を Fizz, Buzz, FizzBuzz に変換するのが一般的ですが、拙作の for を使ったプログラムは 0 から始めているので、最初に FizzBuzz と表示してしまいます。変数 x の初期値を 1 にしたほうがよかったですね。あとで修正しておきます。

もちろん、FizzBuzz 問題も再帰でプログラムすることができます。

リスト : FizzBuzz 問題 (ISLisp 再帰版)

(defun display (x)
  (format (standard-output) "~A " x))

(defun change-fizzbuzz (x)
  (cond ((= (mod x 15) 0) "FizzBuzz")
        ((= (mod x 3) 0) "Fizz")
        ((= (mod x 5) 0) "Buzz")
        (t (convert x <string>))))

(defun fizzbuzz (x)
  (cond ((<= x 100)
         (display (change-fizzbuzz x))
         (fizzbuzz (+ x 1)))))
Easy-ISLisp Ver1.66
> (load "fizzbuzz.lsp")
T
> (fizzbuzz 1)
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fizz 22 23 Fizz Buzz 
26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz 37 38 Fizz Buzz 41 Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz 
Fizz 52 53 Fizz Buzz 56 Fizz 58 59 FizzBuzz 61 62 Fizz 64 Buzz Fizz 67 68 Fizz Buzz 71 Fizz 73 74 FizzBuzz 
76 77 Fizz 79 Buzz Fizz 82 83 Fizz Buzz 86 Fizz 88 89 FizzBuzz 91 92 Fizz 94 Buzz Fizz 97 98 Fizz Buzz NIL

s から e までの整数列を生成する関数 iota を用意すると、mapcar で FizzBuzz 問題を解くことができます。拙作のページでは iota を繰り返し (for) で作成しましたが、再帰でプログラムすると次のようになります。

> (defun iota (s e) (if (> s e) nil (cons s (iota (+ s 1) e))))
IOTA
> (iota 1 10)
(1 2 3 4 5 6 7 8 9 10)
> (mapcar #'change-fizzbuzz (iota 1 100))
("1" "2" "Fizz" "4" "Buzz" "Fizz" "7" "8" "Fizz" "Buzz" "11" "Fizz" "13" "14" "FizzBuzz"
 "16" "17" "Fizz" "19" "Buzz" "Fizz" "22" "23" "Fizz" "Buzz" "26" "Fizz" "28" "29" "FizzBuzz" 
 "31" "32" "Fizz" "34" "Buzz" "Fizz" "37" "38" "Fizz" "Buzz" "41" "Fizz" "43" "44" "FizzBuzz" 
 "46" "47" "Fizz" "49" "Buzz" "Fizz" "52" "53" "Fizz" "Buzz" "56" "Fizz" "58" "59" "FizzBuzz" 
 "61" "62" "Fizz" "64" "Buzz" "Fizz" "67" "68" "Fizz" "Buzz" "71" "Fizz" "73" "74" "FizzBuzz" 
 "76" "77" "Fizz" "79" "Buzz" "Fizz" "82" "83" "Fizz" "Buzz" "86" "Fizz" "88" "89" "FizzBuzz" 
 "91" "92" "Fizz" "94" "Buzz" "Fizz" "97" "98" "Fizz" "Buzz")

ただし、この iota は末尾再帰ではないので、大きなリストを生成するとエラーになるかもしれません。興味のある方は末尾再帰で書き直してみてください。


2020 年 4 月

4月25日

●fold と scan

関数型言語で高階関数といえば、マップ (map)、フィルター (filter)、畳み込み (fold, reduce) などが有名です。最近では、無名関数 (ラムダ式) をサポートするプログラミング言語が増えているので、map や fold の知名度も上昇しているのではないでしょうか。fold はリストの要素に関数を適用してその結果を返しますが、計算途中の累積値をリストに格納して返す関数を定義することもできます。高階関数 -Wikipedia によると、これを prefix scan (単に scan) と呼ぶそうです。

scan が高階関数といわれても、馴染みのない方が多いかもしれませんね。scan というと M.Hiroi はC言語の標準ライブラリ関数 scanf を思い出します。高階関数 scan を標準で用意している処理系は、関数型言語でもそれほど多くはないと思います。M.Hiroi が知っているところでは Haskell (scanl, scanr) や Scala (scanLeft, scanRight) ぐらいでしょうか。

M.Hiroi's Home Page では、以下のページで scanl (scan-left) と scanr (scan-right) を取り上げています。

Gauche (Scheme) のプログラムと実行例を示します。

リスト : fold と scan

;;; fold
(define (fold-left fn a xs)
  (if (null? xs)
      a
      (fold-left fn (fn a (car xs)) (cdr xs))))

(define (fold-right fn a xs)
  (if (null? xs)
      a
      (fn (car xs) (fold-right fn a (cdr xs)))))

;;; scan
(define (scanl fn a xs)
  (if (null? xs)
      (list a)
      (cons a (scanl fn (fn a (car xs)) (cdr xs)))))

(define (scanr fn a xs)
  (if (null? xs)
      (list a)
      (let ((ys (scanr fn a (cdr xs))))
        (cons (fn (car xs) (car ys)) ys))))
gosh> (iota 10 1)
(1 2 3 4 5 6 7 8 9 10)

gosh> (fold-left + 0 (iota 10 1))
55
gosh> (fold-right + 0 (iota 10 1))
55
gosh> (scanl + 0 (iota 10 1))
(0 1 3 6 10 15 21 28 36 45 55)
gosh> (scanr + 0 (iota 10 1))
(55 54 52 49 45 40 34 27 19 10 0)

gosh> (fold-left (lambda (x y) (cons y x)) () '(a b c d))
(d c b a)
gosh> (fold-right cons () '(a b c d))
(a b c d)
gosh> (scanl (lambda (x y) (cons y x)) () '(a b c d))
(() (a) (b a) (c b a) (d c b a))
gosh> (scanr cons () '(a b c d))
((a b c d) (b c d) (c d) (d) ())

scanl は末尾要素が fold-left の値になり、scanr は先頭要素が fold-right の値になります。scanl の引数に + と 0 を渡すと、累積度数表を簡単に作成することができます。なお、scanl の遅延ストリーム版 stream-scan-left も簡単に作成することができます。興味のある方は拙作のページ お気楽 Common Lisp 入門: 遅延ストリーム (1) をお読みくださいませ。


2020 年 1 月

1月1日

あけましておめでとうございます

旧年中は大変お世話になりました
本年も M.Hiroi's Home Page をよろしくお願い申し上げます


Copyright (C) 2020 Makoto Hiroi
All rights reserved.

[ Home ]