--- title: isbn13からisbn10を求めるスクリプトを作る(途中) author: kazu634 date: 2008-10-26 wordtwit_post_info: - 'O:8:"stdClass":13:{s:6:"manual";b:0;s:11:"tweet_times";i:1;s:5:"delay";i:0;s:7:"enabled";i:1;s:10:"separation";s:2:"60";s:7:"version";s:3:"3.7";s:14:"tweet_template";b:0;s:6:"status";i:2;s:6:"result";a:0:{}s:13:"tweet_counter";i:2;s:13:"tweet_log_ids";a:1:{i:0;i:4353;}s:9:"hash_tags";a:0:{}s:8:"accounts";a:1:{i:0;s:7:"kazu634";}}' categories: - gauche - Lisp ---

少しずつ慣れてきたというのと、文字列のところまで勉強を進めてきたので、自力で作ってみます。

アルゴリズム

作ろうと考えている関数は、isbn13を引数に取ると、isbn10を戻り値とする関数です。isbn10とisbn13というのは、

ISBN-10は、

ISBN● – AAAA – BBBB – C

のように表示される。しかし、●、A、Bの各部分の割り当て桁数は決まっておらず、合計で9桁(必ず1桁のC部分を入れると10桁)となる範囲内で、それぞれの部分は増減する。

それぞれの部分の意味は、

ISBN – Wikipedia

概要

ISBNは13桁のコードで表され、通常5つのパートからなる。

ISBNnnn – ● – AAAA – BBBB – C

●、A、Bの各部分の桁数は決まっておらず、合計で9桁の範囲内でそれぞれの部分は増減する。

各パートの間は、ハイフン(またはスペース)で区切りを付けるのが正式な表示法である。(区切りを付けなくても書籍を特定する上での問題はない。)

チェックデジット

現行規格のISBN(ISBN-13)のチェックデジットは、JANコードと同じく、「モジュラス10 ウェイト3・1(モジュラス10 ウェイト3)」という計算法にて算出される。(チェックデジットを除いた一番左側の桁から順に1、3、1、3…を掛けてそれらの和を取る。和を10で割って出た余りを10から引く。ただし、10で割って出た余りの下1桁が0の場合はチェック数字を0とする。)

ここで、例として ISBN978-4-10-109205-□ のチェックデジット(□部分)を求めてみる。

9×1 + 7×3 + 8×1 + 4×3 + 1×1 + 0×3 + 1×1 + 0×3 + 9×1 + 2×3 + 0×1 + 5×3

= 9 + 21 + 8 + 12 + 1 + 0 + 1 + 0 + 9 + 6 + 0 + 15

= 82

82 ÷ 10 = 8 あまり 2

10 – 2 = 8

よって、このISBNのチェックデジットは 8 である。

ISBN – Wikipedia

この二つを読み比べると結果的に、

  1. isbn13の4~12桁目の数字が、isbn10の1~9桁目の数字になる。
  2. そうして求めた9桁の数字からチェックディジットを求める。

という二つの段階を踏めばいいことがわかります。

数字が何桁の数字かを調べる

引数が13桁の数字かどうかを判断しないと話にならないので、

  1. 引数が13桁の数字である→isbn10を求める処理
  2. 13桁の数字ではない→エラー処理

ということで、書いてみました。

(define (isbn arg)
;; 引数が13桁の数字であれば
(if (rxmatch #/\d\d\d\d\d\d\d\d\d\d\d\d\d/ (number->string arg))
(calc_isbn10) ;; isbn10を求める処理
(print "Not match")))

数字のままだと桁数がどのくらいあるかを調べる方法がわからなかったので、文字列に変換する。そうすると正規表現が使えて幸せになれそうだ(Perlだとコンテクストという考え方があるから、勝手に空気を読んで型変換をしてくれるけど、Gacuheだと明示的にnumber->stringで型変換をする必要がある)。

数字を桁数の多い順にリストに入れていく

m~n桁目(m < n)を抜き出すという処理を数字のままだとできない(と思う)ので、リストかベクタにしようとする。考えたのは数字→文字列→リストという変換でした。

;; number->list内部で使う手続きを定義。
;; 数字を受け取り、文字列に変換→文字列をリストに変換
;; そうしてできたリストの要素を数値に変換する
(define (number->list number)
;; 文字のリストを引数に取り、数値のリストに返還する手続き
(define (loop lis)
(cond [(null? lis) '()]
[else (cons (digit->integer (car lis)) (loop (cdr lis)))]))
(let ((num_list (string->list (number->string number))))
(loop num_list)))
gosh> (number->list 12345)
(1 2 3 4 5)