循環リストの応用例
最近、ISBNの入力ミスを発見するためのスクリプトを書いた。循環リストを使った美しいコードだと思う。
だけど、「循環リスト(Circular list)なんて観念で、
(理論の)完全性のために存在しているだけ」と言われることがある。
また、Googleで「循環リスト」を検索すると
関連検索として「循環リスト 応用例」を提案してくる。
でも、循環リストを使わないと実現しにくいアルゴリズムもあるし、
循環リストはSchemeの楽しさを高めている思っている。
そして私はこよなくschemeを愛している。
だから、ここに実用的な「循環リストの応用例」として
ISBNチェッカーを公開する。
ISBNチェッカー
ISBNは書籍を一意に示すための番号である。ISBN-13の最後の1桁はチェックデジットであり、以下のように計算される。
----- -----
現行規格のISBN (ISBN-13) のチェックディジットは、JANコードと同じく、「モジュラス10 ウェイト3・1(モジュラス10 ウェイト3)」という計算法にて算出される。(チェックディジットを除いた一番左側の桁から順に1、3、1、3…を掛けてそれらの和を取る。和を10で割って出た余りを10から引く。ただし、10で割って出た余りの下1桁が0の場合はチェック数字を0とする。)
----- -----
「ISBN」(2018年12月21日 (金) 08:59UTCの版)『ウィキペディア日本語版』より引用[注1]。
- [注1] ISBNのチェックディジットの計算法の一次ソースは、
International ISBN Agency (2012). ISBN Users' manual (PDF). isbn-international.org (Sixth International ed.).
であり、
A1.1 Calculating the Check Digit
に同内容ことが書かれていることは自分で確認したが、
翻訳が面倒だったので、wikipediaから引用した。
これを実装すると以下のようになる。ただし、入力は文字列として受け取る。
(use srfi-1)
(define (check-isbn13 num)
(define weight (circular-list 1 3))
(zero? (remainder (fold + 0 (map (lambda (x y) (* x y))
weight
(map digit->integer (string->list num))))
10)))
左から1桁ずつループして拾っているのだけど、
掛ける数が1と3で交互なので、
(1 3 1 3 ...)となっている循環リストに対して、
carで先頭を拾って使い、cdrして残りを次のiterationに渡している。
場合分けとかフラグとかを使っていないところが美しいでしょう?
うさぎとかめのアルゴリズム
ところで、あるリストを与えられた時、それが循環リストになっているかをチェックする コードが以下にある。-
http://www.geocities.jp/m_hiroi/func/abcscm14.html
お気楽 Scheme プログラミング入門、 ●循環リストのチェック(2019年2月1日アクセス)
以上