ISBN13 check digit: Difference between revisions
(→{{header|AppleScript}}: Added a draft in Applescript) |
|||
Line 15: | Line 15: | ||
<br><br> |
<br><br> |
||
=={{header|AppleScript}}== |
|||
<lang applescript>-- isISBN13 :: String -> Bool |
|||
on isISBN13(s) |
|||
script digitValue |
|||
on |λ|(c) |
|||
if isDigit(c) then |
|||
{c as integer} |
|||
else |
|||
{} |
|||
end if |
|||
end |λ| |
|||
end script |
|||
set digits to concatMap(digitValue, characters of s) |
|||
13 = length of digits ¬ |
|||
and 0 = sum(zipWith(my mul, digits, cycle({1, 3}))) mod 10 |
|||
end isISBN13 |
|||
---------------------------TEST---------------------------- |
|||
on run |
|||
script test |
|||
on |λ|(s) |
|||
{s, isISBN13(s)} |
|||
end |λ| |
|||
end script |
|||
map(test, {"978-1734314502", "978-1734314509", ¬ |
|||
"978-1788399081", "978-1788399083"}) |
|||
end run |
|||
---------------------GENERIC FUNCTIONS--------------------- |
|||
-- concatMap :: (a -> [b]) -> [a] -> [b] |
|||
on concatMap(f, xs) |
|||
set lng to length of xs |
|||
set acc to {} |
|||
tell mReturn(f) |
|||
repeat with i from 1 to lng |
|||
set acc to acc & (|λ|(item i of xs, i, xs)) |
|||
end repeat |
|||
end tell |
|||
return acc |
|||
end concatMap |
|||
-- cycle :: [a] -> Generator [a] |
|||
on cycle(xs) |
|||
script |
|||
property lng : 1 + (length of xs) |
|||
property i : missing value |
|||
on |λ|() |
|||
if missing value is i then |
|||
set i to 1 |
|||
else |
|||
set nxt to (1 + i) mod lng |
|||
if 0 = ((1 + i) mod lng) then |
|||
set i to 1 |
|||
else |
|||
set i to nxt |
|||
end if |
|||
end if |
|||
return item i of xs |
|||
end |λ| |
|||
end script |
|||
end cycle |
|||
-- foldl :: (a -> b -> a) -> a -> [b] -> a |
|||
on foldl(f, startValue, xs) |
|||
tell mReturn(f) |
|||
set v to startValue |
|||
set lng to length of xs |
|||
repeat with i from 1 to lng |
|||
set v to |λ|(v, item i of xs, i, xs) |
|||
end repeat |
|||
return v |
|||
end tell |
|||
end foldl |
|||
-- isDigit :: Char -> Bool |
|||
on isDigit(c) |
|||
set n to (id of c) |
|||
48 ≤ n and 57 ≥ n |
|||
end isDigit |
|||
-- length :: [a] -> Int |
|||
on |length|(xs) |
|||
set c to class of xs |
|||
if list is c or string is c then |
|||
length of xs |
|||
else |
|||
(2 ^ 29 - 1) -- (maxInt - simple proxy for non-finite) |
|||
end if |
|||
end |length| |
|||
-- map :: (a -> b) -> [a] -> [b] |
|||
on map(f, xs) |
|||
-- The list obtained by applying f |
|||
-- to each element of xs. |
|||
tell mReturn(f) |
|||
set lng to length of xs |
|||
set lst to {} |
|||
repeat with i from 1 to lng |
|||
set end of lst to |λ|(item i of xs, i, xs) |
|||
end repeat |
|||
return lst |
|||
end tell |
|||
end map |
|||
-- min :: Ord a => a -> a -> a |
|||
on min(x, y) |
|||
if y < x then |
|||
y |
|||
else |
|||
x |
|||
end if |
|||
end min |
|||
-- mReturn :: First-class m => (a -> b) -> m (a -> b) |
|||
on mReturn(f) |
|||
-- 2nd class handler function lifted into 1st class script wrapper. |
|||
if script is class of f then |
|||
f |
|||
else |
|||
script |
|||
property |λ| : f |
|||
end script |
|||
end if |
|||
end mReturn |
|||
-- mul (*) :: Num a => a -> a -> a |
|||
on mul(a, b) |
|||
a * b |
|||
end mul |
|||
-- sum :: [Num] -> Num |
|||
on sum(xs) |
|||
script add |
|||
on |λ|(a, b) |
|||
a + b |
|||
end |λ| |
|||
end script |
|||
foldl(add, 0, xs) |
|||
end sum |
|||
-- take :: Int -> [a] -> [a] |
|||
-- take :: Int -> String -> String |
|||
on take(n, xs) |
|||
set c to class of xs |
|||
if list is c then |
|||
if 0 < n then |
|||
items 1 thru min(n, length of xs) of xs |
|||
else |
|||
{} |
|||
end if |
|||
else if string is c then |
|||
if 0 < n then |
|||
text 1 thru min(n, length of xs) of xs |
|||
else |
|||
"" |
|||
end if |
|||
else if script is c then |
|||
set ys to {} |
|||
repeat with i from 1 to n |
|||
set v to |λ|() of xs |
|||
if missing value is v then |
|||
return ys |
|||
else |
|||
set end of ys to v |
|||
end if |
|||
end repeat |
|||
return ys |
|||
else |
|||
missing value |
|||
end if |
|||
end take |
|||
-- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] |
|||
on zipWith(f, xs, ys) |
|||
set lng to min(|length|(xs), |length|(ys)) |
|||
if 1 > lng then return {} |
|||
set xs_ to take(lng, xs) -- Allow for non-finite |
|||
set ys_ to take(lng, ys) -- generators like cycle etc |
|||
set lst to {} |
|||
tell mReturn(f) |
|||
repeat with i from 1 to lng |
|||
set end of lst to |λ|(item i of xs_, item i of ys_) |
|||
end repeat |
|||
return lst |
|||
end tell |
|||
end zipWith</lang> |
|||
{{Out}} |
|||
<pre>{{"978-1734314502", true}, {"978-1734314509", false}, {"978-1788399081", true}, {"978-1788399083", false}}</pre> |
|||
=={{header|AWK}}== |
=={{header|AWK}}== |
Revision as of 14:55, 7 April 2020
- Task
Validate the check digit of an ISBN-13 code. Multiply every other digit by 3. Add the digits together. Take the remainder of division by 10. If it is 0, the ISBN-13 check digit is correct.
Use the following codes for testing:
978-1734314502: good 978-1734314509: bad 978-1788399081: good 978-1788399083: bad
Show output here, on this page
- See also
See https://isbn-information.com/the-13-digit-isbn.html for details on the method of validation.
AppleScript
<lang applescript>-- isISBN13 :: String -> Bool on isISBN13(s)
script digitValue on |λ|(c) if isDigit(c) then {c as integer} else {} end if end |λ| end script set digits to concatMap(digitValue, characters of s) 13 = length of digits ¬ and 0 = sum(zipWith(my mul, digits, cycle({1, 3}))) mod 10
end isISBN13
TEST----------------------------
on run
script test on |λ|(s) {s, isISBN13(s)} end |λ| end script map(test, {"978-1734314502", "978-1734314509", ¬ "978-1788399081", "978-1788399083"})
end run
GENERIC FUNCTIONS---------------------
-- concatMap :: (a -> [b]) -> [a] -> [b] on concatMap(f, xs)
set lng to length of xs set acc to {} tell mReturn(f) repeat with i from 1 to lng set acc to acc & (|λ|(item i of xs, i, xs)) end repeat end tell return acc
end concatMap
-- cycle :: [a] -> Generator [a] on cycle(xs)
script property lng : 1 + (length of xs) property i : missing value on |λ|() if missing value is i then set i to 1 else set nxt to (1 + i) mod lng if 0 = ((1 + i) mod lng) then set i to 1 else set i to nxt end if end if return item i of xs end |λ| end script
end cycle
-- foldl :: (a -> b -> a) -> a -> [b] -> a on foldl(f, startValue, xs)
tell mReturn(f) set v to startValue set lng to length of xs repeat with i from 1 to lng set v to |λ|(v, item i of xs, i, xs) end repeat return v end tell
end foldl
-- isDigit :: Char -> Bool on isDigit(c)
set n to (id of c) 48 ≤ n and 57 ≥ n
end isDigit
-- length :: [a] -> Int on |length|(xs)
set c to class of xs if list is c or string is c then length of xs else (2 ^ 29 - 1) -- (maxInt - simple proxy for non-finite) end if
end |length|
-- map :: (a -> b) -> [a] -> [b] on map(f, xs)
-- The list obtained by applying f -- to each element of xs. tell mReturn(f) set lng to length of xs set lst to {} repeat with i from 1 to lng set end of lst to |λ|(item i of xs, i, xs) end repeat return lst end tell
end map
-- min :: Ord a => a -> a -> a on min(x, y)
if y < x then y else x end if
end min
-- mReturn :: First-class m => (a -> b) -> m (a -> b) on mReturn(f)
-- 2nd class handler function lifted into 1st class script wrapper. if script is class of f then f else script property |λ| : f end script end if
end mReturn
-- mul (*) :: Num a => a -> a -> a on mul(a, b)
a * b
end mul
-- sum :: [Num] -> Num on sum(xs)
script add on |λ|(a, b) a + b end |λ| end script foldl(add, 0, xs)
end sum
-- take :: Int -> [a] -> [a] -- take :: Int -> String -> String on take(n, xs)
set c to class of xs if list is c then if 0 < n then items 1 thru min(n, length of xs) of xs else {} end if else if string is c then if 0 < n then text 1 thru min(n, length of xs) of xs else "" end if else if script is c then set ys to {} repeat with i from 1 to n set v to |λ|() of xs if missing value is v then return ys else set end of ys to v end if end repeat return ys else missing value end if
end take
-- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] on zipWith(f, xs, ys)
set lng to min(|length|(xs), |length|(ys)) if 1 > lng then return {} set xs_ to take(lng, xs) -- Allow for non-finite set ys_ to take(lng, ys) -- generators like cycle etc set lst to {} tell mReturn(f) repeat with i from 1 to lng set end of lst to |λ|(item i of xs_, item i of ys_) end repeat return lst end tell
end zipWith</lang>
- Output:
{{"978-1734314502", true}, {"978-1734314509", false}, {"978-1788399081", true}, {"978-1788399083", false}}
AWK
<lang AWK>
- syntax: GAWK -f ISBN13_CHECK_DIGIT.AWK
BEGIN {
arr[++n] = "978-1734314502" arr[++n] = "978-1734314509" arr[++n] = "978-1788399081" arr[++n] = "978-1788399083" arr[++n] = "9780820424521" arr[++n] = "0820424528" for (i=1; i<=n; i++) { printf("%s %s\n",arr[i],isbn13(arr[i])) } exit(0)
} function isbn13(isbn, check_digit,i,sum) {
gsub(/[ -]/,"",isbn) if (length(isbn) != 13) { return("NG length") } for (i=1; i<=12; i++) { sum += substr(isbn,i,1) * (i % 2 == 1 ? 1 : 3) } check_digit = 10 - (sum % 10) return(substr(isbn,13,1) == check_digit ? "OK" : sprintf("NG check digit S/B %d",check_digit))
} </lang>
- Output:
978-1734314502 OK 978-1734314509 NG check digit S/B 2 978-1788399081 OK 978-1788399083 NG check digit S/B 1 9780820424521 OK 0820424528 NG length
C
<lang c>#include <stdio.h>
int check_isbn13(const char *isbn) {
int ch = *isbn, count = 0, sum = 0; /* check isbn contains 13 digits and calculate weighted sum */ for ( ; ch != 0; ch = *++isbn, ++count) { /* skip hyphens or spaces */ if (ch == ' ' || ch == '-') { --count; continue; } if (ch < '0' || ch > '9') { return 0; } if (count & 1) { sum += 3 * (ch - '0'); } else { sum += ch - '0'; } } if (count != 13) return 0; return !(sum%10);
}
int main() {
int i; const char* isbns[] = {"978-1734314502", "978-1734314509", "978-1788399081", "978-1788399083"}; for (i = 0; i < 4; ++i) { printf("%s: %s\n", isbns[i], check_isbn13(isbns[i]) ? "good" : "bad"); } return 0;
}</lang>
- Output:
978-1734314502: good 978-1734314509: bad 978-1788399081: good 978-1788399083: bad
Factor
<lang factor>USING: combinators.short-circuit formatting kernel math math.functions math.parser math.vectors qw sequences sequences.extras sets unicode ;
- (isbn13?) ( str -- ? )
string>digits [ <evens> sum ] [ <odds> 3 v*n sum + ] bi 10 divisor? ;
- isbn13? ( str -- ? )
"- " without { [ length 13 = ] [ [ digit? ] all? ] [ (isbn13?) ] } 1&& ;
qw{ 978-1734314502 978-1734314509 978-1788399081 978-1788399083 } [ dup isbn13? "good" "bad" ? "%s: %s\n" printf ] each</lang>
- Output:
978-1734314502: good 978-1734314509: bad 978-1788399081: good 978-1788399083: bad
Go
<lang go>package main
import (
"fmt" "strings" "unicode/utf8"
)
func checkIsbn13(isbn string) bool {
// remove any hyphens or spaces isbn = strings.ReplaceAll(strings.ReplaceAll(isbn, "-", ""), " ", "") // check length == 13 le := utf8.RuneCountInString(isbn) if le != 13 { return false } // check only contains digits and calculate weighted sum sum := int32(0) for i, c := range isbn { if c < '0' || c > '9' { return false } if i%2 == 0 { sum += c - '0' } else { sum += 3 * (c - '0') } } return sum%10 == 0
}
func main() {
isbns := []string{"978-1734314502", "978-1734314509", "978-1788399081", "978-1788399083"} for _, isbn := range isbns { res := "bad" if checkIsbn13(isbn) { res = "good" } fmt.Printf("%s: %s\n", isbn, res) }
}</lang>
- Output:
978-1734314502: good 978-1734314509: bad 978-1788399081: good 978-1788399083: bad
Haskell
<lang haskell>import Data.Char (isDigit, digitToInt) import Control.Monad (forM_) import Text.Printf (printf)
testISBNs :: [String] testISBNs = ["978-1734314502", "978-1734314509", "978-1788399081", "978-1788399083"]
pair :: Num a => [a] -> [(a, a)] pair [] = [] pair xs = p (take 2 xs) : pair (drop 2 xs)
where p ps = case ps of (x:y:zs) -> (x,y) (x:zs) -> (x,0)
validIsbn13 :: String -> Bool validIsbn13 isbn
| length (digits isbn) /= 13 = False | otherwise = calc isbn `rem` 10 == 0 where digits = map digitToInt . filter isDigit calc = foldl (\a (x, y) -> x + y * 3 + a) 0 . pair . digits
main :: IO () main = forM_ testISBNs (\isbn -> printf "%s: Valid: %s\n" isbn (show $ validIsbn13 isbn))</lang>
- Output:
978-1734314502: Valid: True 978-1734314509: Valid: False 978-1788399081: Valid: True 978-1788399083: Valid: False
Or, expressed in terms of cycle:
<lang haskell>import Data.Char (digitToInt, isDigit)
isISBN13 :: String -> Bool isISBN13 =
(0 ==) . flip rem 10 . sum . flip (zipWith ((*) . digitToInt) . filter isDigit) (cycle [1, 3])
main :: IO () main =
mapM_ print $ ((,) <*> isISBN13) <$> ["978-1734314502", "978-1734314509", "978-1788399081", "978-1788399083"]</lang>
- Output:
("978-1734314502",True) ("978-1734314509",False) ("978-1788399081",True) ("978-1788399083",False)
Julia
<lang julia>function isbncheck(str)
return sum(iseven(i) ? 3 * parse(Int, ch) : parse(Int, ch) for (i, ch) in enumerate(replace(str, r"\D" => ""))) % 10 == 0
end
const testingcodes = ["978-1734314502", "978-1734314509",
"978-1788399081", "978-1788399083"]
for code in testingcodes
println(code, ": ", isbncheck(code) ? "good" : "bad")
end
</lang>
- Output:
978-1734314502: good 978-1734314509: bad 978-1788399081: good 978-1788399083: bad
langur
In this example, we map to multiple functions (actually 1 no-op). <lang langur>val .isbn13checkdigit = f(var .s) {
.s = replace(.s, RE/[\- ]/) matching(re/^[0-9]{13}$/, .s) and fold(f{+}, map [_, f{x 3}], s2n .s) div 10
}
val .tests = h{
"978-1734314502": true, "978-1734314509": false, "978-1788399081": true, "978-1788399083": false,
}
for .key of .tests {
val .pass = .isbn13checkdigit(.key) write .key, ": ", if(.pass: "good"; "bad") writeln if(.pass == .tests[.key]: ""; " (ISBN-13 CHECK DIGIT TEST FAILED)")
}</lang>
In this example, we set a for loop value as it progresses. <lang langur>val .isbn13checkdigit = f(var .s) {
.s = replace(.s, RE/[\- ]/) var .alt = true matching(re/^[0-9]{13}$/, .s) and for[=0] .d in s2n(.s) { _for += if(not= .alt: .d x 3; .d) } div 10
}
val .tests = h{
"978-1734314502": true, "978-1734314509": false, "978-1788399081": true, "978-1788399083": false,
}
for .key of .tests {
val .pass = .isbn13checkdigit(.key) write .key, ": ", if(.pass: "good"; "bad") writeln if(.pass == .tests[.key]: ""; " (ISBN-13 CHECK DIGIT TEST FAILED)")
}</lang>
- Output:
978-1734314502: good 978-1734314509: bad 978-1788399081: good 978-1788399083: bad
Nanoquery
<lang nanoquery>def checkIsbn13(isbn)
// remove any hyphens or spaces isbn = str(isbn).replace("-","").replace(" ","")
// check length = 13 if len(isbn) != 13 return false end
// check only contains digits and calculate weighted sum sum = 0 for i in range(0, len(isbn) - 1) c = isbn[i] if (ord(c) < ord("0")) or (ord(c) > ord("9")) return false end
if (i % 2) = 0 sum += ord(c) - ord("0") else sum += 3 * (ord(c) - ord("0")) end end
return (sum % 10) = 0
end
isbns = {"978-1734314502", "978-1734314509", "978-1788399081", "978-1788399083"} for isbn in isbns
res = "bad" if checkIsbn13(isbn) res = "good" end
print format("%s: %s\n", isbn, res)
end</lang>
- Output:
978-1734314502: good 978-1734314509: bad 978-1788399081: good 978-1788399083: bad
Perl
<lang perl>use strict; use warnings; use feature 'say';
sub check_digit {
my($isbn) = @_; my($sum); $sum += (1,3)[$_%2] * (split , join , split /\D/, $isbn)[$_] for 0..11; (10 - $sum % 10) % 10;
}
for (<978-1734314502 978-1734314509 978-1788399081 978-1788399083 978-2-74839-908-0 978-2-74839-908-5>) {
my($isbn,$check) = /(.*)(.)/; my $check_d = check_digit($isbn); say "$_ : " . ($check == $check_d ? 'Good' : "Bad check-digit $check; should be $check_d")
}</lang>
- Output:
978-1734314502 : Good 978-1734314509 : Bad check-digit 9; should be 2 978-1788399081 : Good 978-1788399083 : Bad check-digit 3; should be 1 978-2-74839-908-0 : Good 978-2-74839-908-5 : Bad check-digit 5; should be 0
Phix
<lang Phix>procedure check_isbn13(string isbn)
integer digits = 0, checksum = 0, w = 1 for i=1 to length(isbn) do integer ch = isbn[i] if ch!=' ' and ch!='-' then ch -= '0' if ch<0 or ch>9 then checksum = 9 exit end if checksum += ch*w digits += 1 w = 4-w end if end for checksum = remainder(checksum,10) string gb = iff(digits=13 and checksum=0 ? "good" : "bad") printf(1,"%s: %s\n",{isbn,gb})
end procedure
constant isbns = {"978-1734314502", "978-1734314509", "978-1788399081", "978-1788399083",
"978-2-74839-908-0","978-2-74839-908-5","978 1 86197 876 9"}
for i=1 to length(isbns) do check_isbn13(isbns[i]) end for</lang>
- Output:
978-1734314502: good 978-1734314509: bad 978-1788399081: good 978-1788399083: bad 978-2-74839-908-0: good 978-2-74839-908-5: bad 978 1 86197 876 9: good
PicoLisp
<lang PicoLisp>(de isbn13? (S)
(let L (make (for N (chop S) (and (format N) (link @)) ) ) (and (= 13 (length L)) (=0 (% (sum * L (circ 1 3)) 10)) ) ) )
(mapc
'((A) (tab (-19 1) A (if (isbn13? A) 'ok 'fail) ) ) (quote "978-1734314502" "978-1734314509" "978-1-86197-876-9" "978-2-74839-908-5" "978 1 86197 876 9" ) )</lang>
- Output:
978-1734314502 ok 978-1734314509 fail 978-1-86197-876-9 ok 978-2-74839-908-5 fail 978 1 86197 876 9 ok
Python
<lang python>def is_isbn13(n):
n = n.replace('-',).replace(' ', ) if len(n) != 13: return False product = (sum(int(ch) for ch in n[::2]) + sum(int(ch) * 3 for ch in n[1::2])) return product % 10 == 0
if __name__ == '__main__':
tests =
978-1734314502 978-1734314509 978-1788399081 978-1788399083.strip().split()
for t in tests: print(f"ISBN13 {t} validates {is_isbn13(t)}")</lang>
- Output:
ISBN13 978-1734314502 validates True ISBN13 978-1734314509 validates False ISBN13 978-1788399081 validates True ISBN13 978-1788399083 validates False
Or, expressed in terms of itertools.cycle
<lang python>ISBN13 check digit
from itertools import cycle from operator import mul
- isISBN13 :: String -> Bool
def isISBN13(s):
True if the digits of s form a valid ISBN-13 code. digits = [int(c) for c in s if c.isdigit()] return 13 == len(digits) and 0 == sum( map( mul, digits, cycle([1, 3]) ) ) % 10
- ---------------------------TEST---------------------------
- main :: IO ()
def main():
Validation of four strings:
for s in ['978-1734314502', '978-1734314509', '978-1788399081', '978-1788399083']: print((s, isISBN13(s)))
- MAIN ---
if __name__ == '__main__':
main()</lang>
- Output:
('978-1734314502', True) ('978-1734314509', False) ('978-1788399081', True) ('978-1788399083', False)
Raku
(formerly Perl 6)
Also test a value that has a zero check digit.
<lang perl6>sub check-digit ($isbn) {
(10 - (sum (|$isbn.comb(/<[0..9]>/)) »*» (1,3)) % 10).substr: *-1
}
{
my $check = .substr(*-1); my $check-digit = check-digit .chop; say "$_ : ", $check == $check-digit ?? 'Good' !! "Bad check-digit $check; should be $check-digit"
} for words <
978-1734314502 978-1734314509 978-1788399081 978-1788399083 978-2-74839-908-0 978-2-74839-908-5
>;</lang>
- Output:
978-1734314502 : Good 978-1734314509 : Bad check-digit 9; should be 2 978-1788399081 : Good 978-1788399083 : Bad check-digit 3; should be 1 978-2-74839-908-0 : Good 978-2-74839-908-5 : Bad check-digit 5; should be 0
REXX
A couple of additional checks were made to verify a correct length, and also that the ISBN-13 code is all numerics (with optional minus signs). <lang rexx>/*REXX pgm validates the check digit of an ISBN─13 code (it may have embedded minuses).*/ parse arg $ /*obtain optional arguments from the CL*/ if $= | if $="," then $= '978-1734314502 978-1734314509 978-1788399081 978-1788399083' @ISBN= "ISBN─13 code isn't" /*a literal used when displaying msgs. */
/* [↓] remove all minuses from X code.*/ do j=1 for words($); y= word($,j) /*obtain an ISBN─13 code from $ list.*/ x= space( translate(y, , '-'), 0) /*remove all minus signs from the code.*/ L= length(x) /*obtain the length of the ISBN-13 code*/ if L \== 13 then do; say @ISBN '13 characters: ' x; exit 13; end if verify(x, 9876543210)\==0 then do; say @ISBN 'numeric: ' x; exit 10; end sum= 0 do k=1 for L; #= substr(x, k, 1) /*get a decimal digit from the X code. */ if \(k//2) then #= # * 3 /*multiply every other digit by three. */ sum= sum + # /*add the digit (or product) to the SUM*/ end /*k*/
if right(sum, 1)==0 then say ' ISBN-13 code ' x " is valid." else say ' ISBN-13 code ' x " isn't valid." end /*j*/ /*stick a fork in it, we're all done. */</lang>
- output when using the four default inputs:
ISBN-13 code 9781734314502 is valid. ISBN-13 code 9781734314509 isn't valid. ISBN-13 code 9781788399081 is valid. ISBN-13 code 9781788399083 isn't valid.
Swift
<lang swift>func checkISBN(isbn: String) -> Bool {
guard !isbn.isEmpty else { return false }
let sum = isbn .compactMap({ $0.wholeNumberValue }) .enumerated() .map({ $0.offset & 1 == 1 ? 3 * $0.element : $0.element }) .reduce(0, +)
return sum % 10 == 0
}
let cases = [
"978-1734314502", "978-1734314509", "978-1788399081", "978-1788399083"
]
for isbn in cases {
print("\(isbn) => \(checkISBN(isbn: isbn) ? "good" : "bad")")
}</lang>
- Output:
978-1734314502 => good 978-1734314509 => bad 978-1788399081 => good 978-1788399083 => bad
XPL0
<lang XPL0>include xpllib; \contains StrLen function
proc ISBN13(Str); \Show if International Standard Book Number is good char Str; int Sum, Cnt, Dig, I; [Sum:= 0; Cnt:= 0; for I:= 0 to StrLen(Str)-1 do
[Dig:= Str(I) - ^0; if Dig>=0 & Dig<=9 then [Sum:= Sum + Dig; Cnt:= Cnt + 1; if (Cnt&1) = 0 then Sum:= Sum + Dig + Dig; ]; ];
Text(0, Str); Text(0, if rem(Sum/10)=0 & Cnt=13 then ": good" else ": bad"); CrLf(0); ];
[ISBN13("978-1734314502");
ISBN13("978-1734314509"); ISBN13("978-1788399081"); ISBN13("978-1788399083"); ISBN13("978-1-59327-220-3"); ISBN13("978-178839918");
]</lang>
- Output:
978-1734314502: good 978-1734314509: bad 978-1788399081: good 978-1788399083: bad 978-1-59327-220-3: good 978-178839918: bad
zkl
<lang zkl>fcn ISBN13_check(isbn){ // "978-1734314502", throws on invalid digits
var [const] one3=("13"*6 + 1).split("").apply("toInt"); // examine 13 digits // one3=("13"*6) if you want to calculate what the check digit should be one3.zipWith('*,isbn - " -").sum(0) % 10 == 0
}</lang> <lang zkl>isbns:=
- <<<"
978-1734314502 978-1734314509 978-1788399081 978-1788399083 978-2-74839-908-0 978-2-74839-908-5".split("\n");
- <<<
foreach isbn in (isbns)
{ println(isbn.strip()," ",ISBN13_check(isbn) and " Good" or " Bad") }</lang>
- Output:
978-1734314502 Good 978-1734314509 Bad 978-1788399081 Good 978-1788399083 Bad 978-2-74839-908-0 Good 978-2-74839-908-5 Bad