ISBN13 check digit: Difference between revisions
Content deleted Content added
Langurmonkey (talk | contribs) |
Added OmniMark solution, which includes additional conditions to ignore non-ISBN-13 numbers |
||
(11 intermediate revisions by 3 users not shown) | |||
Line 199:
bad</pre>
=={{header|ABC}}==
<syntaxhighlight lang="abc">HOW TO REPORT valid.isbn13 str:
PUT {} IN digits
FOR d IN {0..9}: PUT d IN digits["`d`"]
IF #str <> 14 OR str item 4 <> '-': FAIL
PUT 1, 0 IN mul, sum
FOR c IN str|3 ^ str@5:
IF c not.in keys digits: FAIL
PUT sum + digits[c] * mul IN sum
PUT 4 - mul IN mul
REPORT sum mod 10 = 0
PUT {} IN tests
PUT "978-0596528126" IN tests[1]
PUT "978-0596528120" IN tests[2]
PUT "978-1788399081" IN tests[3]
PUT "978-1788399083" IN tests[4]
FOR test IN tests:
SELECT:
valid.isbn13 test: WRITE test^": good"/
ELSE: WRITE test^": bad"/</syntaxhighlight>
{{out}}
<pre>978-0596528126: good
978-0596528120: bad
978-1788399081: good
978-1788399083: bad</pre>
=={{header|Action!}}==
{{libheader|Action! Tool Kit}}
Line 1,943 ⟶ 1,970:
=={{header|langur}}==
In this example, we map to multiple functions (actually 1 no-op).
<syntaxhighlight lang="langur">
val isbn13checkdigit = fn(var s) {
s -> re/^[0-9]{13}$/ and
fold(fn{+}, map([_, fn{*3}], s2n(s))) div 10
}
val
"978-0596528126": true,
"978-0596528120": false,
Line 1,956 ⟶ 1,984:
}
for
val
write
writeln if(
}
</syntaxhighlight>
{{out}}
Line 2,087 ⟶ 2,095:
978-1788399081 good
978-1788399083 bad</pre>
=={{header|Miranda}}==
<syntaxhighlight lang="miranda">main :: [sys_message]
main = [Stdout (lay (map test tests))]
test :: [char]->[char]
test isbn = isbn ++ ": good", if isbn13 isbn
= isbn ++ ": bad", otherwise
tests :: [[char]]
tests = ["978-0596528126",
"978-0596528120",
"978-1788399081",
"978-1788399083"]
isbn13 :: [char]->bool
isbn13 str = False, if #isbn ~= 13 \/ ~and (map digit isbn)
= check mod 10 = 0, otherwise
where isbn = filter (~= '-') str
digits = map (numval.(:[])) isbn
check = sum (zipwith (*) digits (concat (repeat [1,3])))
uncurry :: (*->**->***)->(*,**)->***
uncurry f (a,b) = f a b
zipwith :: (*->**->***)->[*]->[**]->[***]
zipwith f a b = map (uncurry f) (zip2 a b)</syntaxhighlight>
{{out}}
<pre>978-0596528126: good
978-0596528120: bad
978-1788399081: good
978-1788399083: bad</pre>
=={{header|Modula-2}}==
Line 2,208 ⟶ 2,248:
978-1788399081: good
978-1788399083: bad
</pre>
=={{header|OmniMark}}==
<syntaxhighlight lang="omnimark">
define switch function isbn (value stream vs-s) as
local integer li-total initial {0}
local integer li-check
repeat scan vs-s
; Any digits in an ISBN-13 may be separated by a hyphen or space
match (digit => lpv-1 ('-' | space)? digit => lpv-3 ('-' | space)? ) => p
; Add the first digit to the total and add the second multiplied by 3
increment li-total by (lpv-1 + (lpv-3 * 3))
match any => lpv-c
; This is always the thirteenth digit, the 'check' digit
set li-check to lpv-c
again
do when 10 - li-total modulo 10 = li-check
return true
else when li-total modulo 10 = 0 and li-check = 0
return true
else
return false
done
process
; These are the first four test ISBN-13s from the problem description, with
; the first and third being valid and the second and fourth invalid.
submit "978-0596528126"
submit "978-0596528120"
submit "978-1788399081"
submit "978-1788399083"
; This ISBN-13 is from https://isbn-information.com/the-13-digit-isbn.html
submit "978-1-86197-876-9"
submit "978 1 86197 876 9"
; These ISBNs are from https://en.wikipedia.org/wiki/ISBN
submit "978-3-16-148410-0"
submit "978 3 16 148410 9" ; But this one has had the 'check' digit changed
submit "This is valid: 978-0-306-40615-7^" ; Adding some extra complexity :)
submit "978-03-0640-615 7." ; Formatted as "EAN-Group-Publisher-Title Check"
; These strings show how non-ISBN-13 numbers must be (and are) ignored.
submit "This is a 14-digit number - 555-30640610555 - so it is ignored.%n"
submit "This is an ISBN-10, so it is ignored too: 0-306-40615-2.%n"
; This find rule will only find a string that is an ISBN-13 (digit/-/space)
; combination. It has been split into three lines to make it easier to see
; what is being done.
; 1. The string must either be preceded by any character other than a digit
; or start the stream, and
; 2. It must then have 12 digits (with optional hyphens or spaces
; intermingled) and must conclude with a digit, and
; 3. It either must not be followed by a another digit (making it more
; than 13 digits, so not an ISBN-13) or the stream ends.
find ([any \ digit] | value-start) => lpv-s
((digit ('-' | space)?){12} digit) => lpv-n
([any \ digit] | value-end) => lpv-e
output lpv-s || lpv-n || lpv-e
do when isbn(lpv-n) = true
output ' (good)%n'
else
output ' (bad)%n'
done
; output all other characters verbatim
find any => lpv-char
output lpv-char
</syntaxhighlight>
{{out}}
<pre>
978-0596528126 (good)
978-0596528120 (bad)
978-1788399081 (good)
978-1788399083 (bad)
978-1-86197-876-9 (good)
978 1 86197 876 9 (good)
978-3-16-148410-0 (good)
978 3 16 148410 9 (bad)
This is valid: 978-0-306-40615-7^ (good)
978-03-0640-615 7. (good)
This is a 14-digit number - 555-30640610555 - so it is ignored.
This is an ISBN-10, so it is ignored too: 0-306-40615-2.
</pre>
Line 2,733 ⟶ 2,857:
978-1788399083 - false
</pre>
=={{header|Refal}}==
<syntaxhighlight lang="refal">$ENTRY Go {
= <Test '978-0596528126'>
<Test '978-0596528120'>
<Test '978-1788399081'>
<Test '978-1788399083'>
};
Test {
e.X = <Prout e.X ': ' <ISBN e.X>>;
};
ISBN {
e.X, <Remove '-' e.X>: e.DS,
<CheckDigits e.DS>: {
False = Bad;
True = <ISBN1 e.DS>;
};
};
ISBN1 {
(13 s.Sum), <Mod s.Sum 10>: 0 = Good;
(13 s.Sum) e.X = Bad;
(s.N s.Sum) = Bad;
(s.N s.Sum) s.D e.X,
<+ s.N 1>: s.N1,
<Numb s.D>: s.V,
<Mod s.N 2>: {
0 = <ISBN1 (s.N1 <+ s.Sum s.V>) e.X>;
1 = <ISBN1 (s.N1 <+ s.Sum <* 3 s.V>>) e.X>;
};
e.X = <ISBN1 (0 0) e.X>;
};
Remove {
s.1 = ;
s.1 s.1 e.X = <Remove s.1 e.X>;
s.1 s.2 e.X = s.2 <Remove s.1 e.X>;
};
CheckDigits {
= True;
s.D e.X, '0123456789': e.A s.D e.B = <CheckDigits e.X>;
e.X = False;
};</syntaxhighlight>
{{out}}
<pre>978-0596528126: Good
978-0596528120: Bad
978-1788399081: Good
978-1788399083: Bad</pre>
=={{header|REXX}}==
Line 2,909 ⟶ 3,086:
978-1788399083: FALSE
</pre>
=={{header|SETL}}==
<syntaxhighlight lang="setl">program isbn13;
loop for test in [
"978-0596528126", "978-0596528120",
"978-1788399081", "978-1788399083"
] do
print(test + if valid_isbn13 test then ": good" else ": bad" end);
end loop;
op valid_isbn13(isbn);
if #isbn /= 14
or isbn(4) /= '-'
or exists c in isbn | not c in "0123456789-" then
return false;
end if;
m := 3;
loop for ch in isbn | ch in "0123456789" do
s +:= val ch * (m := 4 - m);
end loop;
return s mod 10 = 0;
end op;
end program;</syntaxhighlight>
{{out}}
<pre>978-0596528126: good
978-0596528120: bad
978-1788399081: good
978-1788399083: bad</pre>
=={{header|Standard ML}}==
===Easy to read version===
<syntaxhighlight lang="sml">(* these type decorations are optional, you could just as well put:
fun isValidISBN s =
*)
fun isValidISBN (s : string) : bool =
let
val digits = List.filter Char.isDigit (explode s)
val nums = map (fn x => ord x - ord #"0") digits
val len = length nums
fun sumISBN [] = raise Domain
| sumISBN [x] = x
| sumISBN (x1::x2::xs) = x1 + 3*x2 + sumISBN xs
in
len = 13 andalso sumISBN nums mod 10 = 0
end
val test = ["978-1734314502", "978-1734314509",
"978-1788399081", "978-1788399083"]
fun printTest (s : string) : unit =
(print s; print (if isValidISBN s then ": good\n" else ": bad\n"))
fun main () = app printTest test</syntaxhighlight>
===Original version===
<syntaxhighlight lang="sml">let
fun check (c, p as (m, n)) =
if Char.isDigit c then
|