blog/content/post/2008/10/26/2008-10-26-00001058.md

248 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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
---
<div class="section">
<p>
少しずつ慣れてきたというのと、文字列のところまで勉強を進めてきたので、自力で作ってみます。
</p>
<p>
<a name="seemore"></a>
</p>
<h4>
アルゴリズム
</h4>
<p>
作ろうと考えている関数は、isbn13を引数に取ると、isbn10を戻り値とする関数です。isbn10とisbn13というのは、
</p>
<blockquote title="ISBN - Wikipedia" cite="http://ja.wikipedia.org/wiki/ISBN">
<p>
ISBN-10は、
</p>
<blockquote>
<p>
ISBN● &#8211; AAAA &#8211; BBBB &#8211; C
</p>
</blockquote>
<p>
のように表示される。しかし、●、A、Bの各部分の割り当て桁数は決まっておらず、合計で9桁必ず1桁のC部分を入れると10桁となる範囲内で、それぞれの部分は増減する。
</p>
<p>
それぞれの部分の意味は、
</p>
<ul>
<li>
●部分 &#8211; 「グループ記号」:出版物の出版された国、地域、言語圏。国別の記号というより、言語別の記号に近い。桁数は、そのグループの出版点数によって異なる。 <ul>
<li>
英語圏は 0 と 1
</li>
<li>
フランス語圏は 2
</li>
<li>
ドイツ語圏は 3ドイツの他、オーストリア、ベルギー、スイスのドイツ語圏を含む
</li>
<li>
日本は 4
</li>
<li>
ロシア(旧・ソビエト連邦)は 5ロシア以外の国では、ベラルーシは 985 、ウクライナは 966 など、他の記号も使用)
</li>
<li>
中国は 7香港は 962
</li>
<li>
その他の国々は 8 番台が2桁チェコスロバキアは 80 、韓国は 89 、など。9番台が2 &#8211; 5桁トルコは 9944 など)を使用している。
</li>
</ul>
</li>
<li>
A部分 &#8211; 「出版者記号」:桁数は、出版社の出版点数などによって異なる。
</li>
<li>
B部分 &#8211; 「書名記号」:出版物に固有の番号。原則として図書の版ごとに付与。
</li>
<li>
C部分 &#8211; 「チェックデジット」検査数字。入力した際に誤りがないか確かめるためのもの。0 &#8211; 9 、X が使用される。( X は数値 10 をあらわす。)
</li>
</ul>
<p>
<cite><a href="http://ja.wikipedia.org/wiki/ISBN" onclick="__gaTracker('send', 'event', 'outbound-article', 'http://ja.wikipedia.org/wiki/ISBN', 'ISBN &#8211; Wikipedia');" target="_blank">ISBN &#8211; Wikipedia</a></cite>
</p>
</blockquote>
<blockquote title="ISBN - Wikipedia" cite="http://ja.wikipedia.org/wiki/ISBN">
<h4>
概要
</h4>
<p>
ISBNは13桁のコードで表され、通常5つのパートからなる。
</p>
<blockquote>
<p>
ISBNnnn &#8211;&#8211; AAAA &#8211; BBBB &#8211; C
</p>
</blockquote>
<p>
●、A、Bの各部分の桁数は決まっておらず、合計で9桁の範囲内でそれぞれの部分は増減する。
</p>
<ul>
<li>
n部分 &#8211; 「接頭記号」: nnn は 978 または 979 のいずれか数字3桁である。
</li>
<li>
●部分 &#8211; 「グループ記号」2006年以前と基本的に同じ。上記解説を参照。ただし、接頭記号が異なれば、グループ記号が同じでも異なる言語圏を指す可能性もある。
</li>
<li>
A部分 &#8211; 「出版者記号」2006年以前と同じ。上記解説を参照。
</li>
<li>
B部分 &#8211; 「書名記号」2006年以前と同じ。上記解説を参照。
</li>
<li>
C部分 &#8211; 「チェックデジット」: 0 &#8211; 9 の数字1桁が入る。以前のISBNのチェックデジットとは計算法が異なり、10桁→13桁に変換する際は再計算が必要となる。
</li>
</ul>
<p>
各パートの間は、ハイフン(またはスペース)で区切りを付けるのが正式な表示法である。(区切りを付けなくても書籍を特定する上での問題はない。)
</p>
<h4>
チェックデジット
</h4>
<p>
現行規格のISBNISBN-13のチェックデジットは、JANコードと同じく、「モジュラス10 ウェイト3・1モジュラス10 ウェイト3」という計算法にて算出される。チェックデジットを除いた一番左側の桁から順に1、3、1、3…を掛けてそれらの和を取る。和を10で割って出た余りを10から引く。ただし、10で割って出た余りの下1桁が0の場合はチェック数字を0とする。
</p>
<p>
ここで、例として ISBN978-4-10-109205-□ のチェックデジット(□部分)を求めてみる。
</p>
<blockquote>
<p>
9×1 + 7×3 + 8×1 + 4×3 + 1×1 + 0×3 + 1×1 + 0×3 + 9×1 + 2×3 + 0×1 + 5×3
</p>
<p>
= 9 + 21 + 8 + 12 + 1 + 0 + 1 + 0 + 9 + 6 + 0 + 15
</p>
<p>
= 82
</p>
<p>
82 ÷ 10 = 8 あまり 2
</p>
<p>
10 &#8211; 2 = 8
</p>
</blockquote>
<p>
よって、このISBNのチェックデジットは 8 である。
</p>
<p>
<cite><a href="http://ja.wikipedia.org/wiki/ISBN" onclick="__gaTracker('send', 'event', 'outbound-article', 'http://ja.wikipedia.org/wiki/ISBN', 'ISBN &#8211; Wikipedia');" target="_blank">ISBN &#8211; Wikipedia</a></cite>
</p>
</blockquote>
<p>
この二つを読み比べると結果的に、
</p>
<ol>
<li>
isbn13の412桁目の数字が、isbn10の19桁目の数字になる。
</li>
<li>
そうして求めた9桁の数字からチェックディジットを求める。
</li>
</ol>
<p>
という二つの段階を踏めばいいことがわかります。
</p>
<h4>
数字が何桁の数字かを調べる
</h4>
<p>
引数が13桁の数字かどうかを判断しないと話にならないので、
</p>
<ol>
<li>
引数が13桁の数字である→isbn10を求める処理
</li>
<li>
13桁の数字ではない→エラー処理
</li>
</ol>
<p>
ということで、書いてみました。
</p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>isbn arg<span class="synSpecial">)</span>
<span class="synComment">;; 引数が13桁の数字であれば</span>
<span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span>rxmatch #/\d\d\d\d\d\d\d\d\d\d\d\d\d/ <span class="synSpecial">(</span>number-&#62;string arg<span class="synSpecial">))</span>
<span class="synSpecial">(</span>calc_isbn10<span class="synSpecial">)</span> <span class="synComment">;; isbn10を求める処理</span>
<span class="synSpecial">(</span><span class="synStatement">print</span> <span class="synConstant">&#34;Not match&#34;</span><span class="synSpecial">)))</span>
</pre>
<p>
数字のままだと桁数がどのくらいあるかを調べる方法がわからなかったので、文字列に変換する。そうすると正規表現が使えて幸せになれそうだPerlだとコンテクストという考え方があるから、勝手に空気を読んで型変換をしてくれるけど、Gacuheだと明示的にnumber->stringで型変換をする必要がある
</p>
<h4>
数字を桁数の多い順にリストに入れていく
</h4>
<p>
mn桁目(m < n)を抜き出すという処理を数字のままだとできないと思うのでリストかベクタにしようとする考えたのは数字文字列リストという変換でした
</p>
<pre class="syntax-highlight">
<span class="synComment">;; number-&#62;list内部で使う手続きを定義。</span>
<span class="synComment">;; 数字を受け取り、文字列に変換→文字列をリストに変換</span>
<span class="synComment">;; そうしてできたリストの要素を数値に変換する</span>
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>number-&#62;list <span class="synStatement">number</span><span class="synSpecial">)</span>
<span class="synComment">;; 文字のリストを引数に取り、数値のリストに返還する手続き</span>
<span class="synSpecial">(</span>define <span class="synSpecial">(</span><span class="synStatement">loop</span> lis<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">cond</span> [<span class="synSpecial">(</span><span class="synStatement">null</span>? lis<span class="synSpecial">)</span> <span class="synSpecial">'()</span>]
[else <span class="synSpecial">(</span><span class="synStatement">cons</span> <span class="synSpecial">(</span>digit-&#62;integer <span class="synSpecial">(</span><span class="synStatement">car</span> lis<span class="synSpecial">))</span> <span class="synSpecial">(</span><span class="synStatement">loop</span> <span class="synSpecial">(</span><span class="synStatement">cdr</span> lis<span class="synSpecial">)))</span>]<span class="synSpecial">))</span>
<span class="synSpecial">(</span><span class="synStatement">let</span> <span class="synSpecial">((</span>num_list <span class="synSpecial">(</span>string-&#62;list <span class="synSpecial">(</span>number-&#62;string <span class="synStatement">number</span><span class="synSpecial">))))</span>
<span class="synSpecial">(</span><span class="synStatement">loop</span> num_list<span class="synSpecial">)))</span>
gosh&#62; <span class="synSpecial">(</span>number-&#62;list <span class="synConstant">12345</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synConstant">1</span> <span class="synConstant">2</span> <span class="synConstant">3</span> <span class="synConstant">4</span> <span class="synConstant">5</span><span class="synSpecial">)</span>
</pre>
</div>