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

アルゴリズム

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

ISBN-10は、

ISBN● – AAAA – BBBB – C

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

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

  • ●部分 – 「グループ記号」:出版物の出版された国、地域、言語圏。国別の記号というより、言語別の記号に近い。桁数は、そのグループの出版点数によって異なる。
    • 英語圏は 0 と 1
    • フランス語圏は 2
    • ドイツ語圏は 3(ドイツの他、オーストリア、ベルギー、スイスのドイツ語圏を含む)
    • 日本は 4
    • ロシア(旧・ソビエト連邦)は 5(ロシア以外の国では、ベラルーシは 985 、ウクライナは 966 など、他の記号も使用)
    • 中国は 7(香港は 962)
    • その他の国々は 8 番台が2桁(チェコスロバキアは 80 、韓国は 89 、など)。9番台が2 – 5桁(トルコは 9944 など)を使用している。
  • A部分 – 「出版者記号」:桁数は、出版社の出版点数などによって異なる。
  • B部分 – 「書名記号」:出版物に固有の番号。原則として図書の版ごとに付与。
  • C部分 – 「チェックデジット」:検査数字。入力した際に誤りがないか確かめるためのもの。0 – 9 、X が使用される。( X は数値 10 をあらわす。)

ISBN – Wikipedia

概要

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

ISBNnnn – ● – AAAA – BBBB – C

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

  • n部分 – 「接頭記号」: nnn は 978 または 979 のいずれか(数字3桁)である。
  • ●部分 – 「グループ記号」:2006年以前と基本的に同じ。上記解説を参照。ただし、接頭記号が異なれば、グループ記号が同じでも異なる言語圏を指す可能性もある。
  • A部分 – 「出版者記号」:2006年以前と同じ。上記解説を参照。
  • B部分 – 「書名記号」:2006年以前と同じ。上記解説を参照。
  • C部分 – 「チェックデジット」: 0 – 9 の数字1桁が入る。以前のISBNのチェックデジットとは計算法が異なり、10桁→13桁に変換する際は再計算が必要となる。

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

チェックデジット

現行規格の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)