ISBN13 check digit: Difference between revisions

From Rosetta Code
Content added Content deleted
(→‎{{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

ISBN13 check digit is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
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>

  1. 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

Works with: langur version 0.8.11

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>

Works with: langur version 0.9.0

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

Translation of: Go

<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


  1. 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


  1. ---------------------------TEST---------------------------
  2. main :: IO ()

def main():

   Validation of four strings:
   for s in ['978-1734314502', '978-1734314509',
             '978-1788399081', '978-1788399083']:
       print((s, isISBN13(s)))


  1. MAIN ---

if __name__ == '__main__':

   main()</lang>
Output:
('978-1734314502', True)
('978-1734314509', False)
('978-1788399081', True)
('978-1788399083', False)

Raku

(formerly Perl 6)

Works with: Rakudo version 2019.11

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:=

  1. <<<"
   978-1734314502
   978-1734314509
   978-1788399081
   978-1788399083
   978-2-74839-908-0
   978-2-74839-908-5".split("\n");
  1. <<<

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