Calcular el dígito de suma de comprobación de Luhn-algoritmo -- beginner campo con haskell campo con checksum camp codereview Relacionados El problema

Calculating Luhn-algorithm checksum digit


23
vote

problema

Español

Hoy decidí aprender algunos Haskell básicos, y para los principiantes, hice un programa para calcular la suma de comprobación de un número de identificación personal sueco. Utiliza el Luhn-Algorithm, también conocido como. IBM MOD-10 .

La explicación de este código se puede encontrar en sueco wikipedia y iGlish Wikipedia

Aquí hay una descripción de cómo funciona el algoritmo:

Dada una cadena de 9 dígitos, abcdefghi1 COMPUTE:

  array = [a*2, b, c*2, d, e*2, f, g*2, h, i*2]   

Then You Computer The sum En esta matriz, así que, por ejemplo, si a*24 es 9988777665544335 , entonces esa parte cuenta como 1 + 6 .

Luego, el resultado es cuánto más tiene que agregar para que sea uniformemente divisible por 10. Por ejemplo, si 9988776655544337 entonces el resultado es 9988777665544338 como 60 - 54 = 6 .

  abcdefghi0  

para probar el código:

  abcdefghi1  

Impresiones:

  abcdefghi2  

Como esta es la primera vez que me las arreglé para hacer algo en Haskell, cualquier comentario es bienvenido. Aunque es la primera vez que no se siente libre de arrancar mi código, aparte de lo que quisiera y sugerir las cosas avanzadas, siempre estoy ansioso por aprender.

Original en ingles

Today I decided to learn some basic Haskell, and for starters I made a program for calculating the checksum of a Swedish personal identification number. It uses the Luhn-algorithm, aka. IBM MOD-10.

Explanation of this code can be found on Swedish Wikipedia and English Wikipedia

Here's a description of how the algorithm works:

Given a string of 9 digits, abcdefghi compute:

array = [a*2, b, c*2, d, e*2, f, g*2, h, i*2] 

Then you computer the sum of the digits in this array, so for example if a*2 is 16, then that part counts as 1 + 6.

Then the result is how much more you have to add to make it evenly divisible by 10. For example, if sum == 54 then the result is 6 as 60 - 54 = 6.

import Data.Char  sumOfChars :: String -> Int sumOfChars "" = 0 sumOfChars str = digitToInt(str !! 0) + sumOfChars(tail str)  twoMult :: Char -> String twoMult c = show (digitToInt(c) * 2)  identificationSum :: String -> Int identificationSum "" = 0 identificationSum str = if length str `mod` 2 == 1 then sumOfChars(twoMult(str !! 0)) + identificationSum(tail str)   else digitToInt(str !! 0) + identificationSum(tail str)  remainingToTens :: Int -> Int remainingToTens x = ceiling(fromIntegral x / 10) * 10 - x  determineLastDigit :: String -> Int determineLastDigit str = remainingToTens(identificationSum(str)) 

To test the code:

determineLastDigit("811228987") 

Prints:

4 

As this is the first time ever I managed to make something in Haskell, any comments are welcome. Even though it is the first time though, feel free to rip my code apart as much as you would like and suggest any advanced things, I am always eager to learn.

        

Lista de respuestas

17
 
vote
vote
La mejor respuesta
 

¡Este es un buen comienzo! Haré dos pases sobre su código, uno para abordar los problemas de estilo, y otro para aprovechar las funciones del preludio Haskell para disminuir la longitud del código y llevarlo más en línea con el uso típico de Haskell y los modismos.

estilo

Su función char[] expect = bi.readLine().toCharArray(); char[] search = bi.readLine().toCharArray(); 7 es un buen ejemplo de la recursión de la cola, pero en Haskell tomaríamos ese 99887766555443318 aparte a través de Match Matching . Un haskell char[] expect = bi.readLine().toCharArray(); char[] search = bi.readLine().toCharArray(); 9 es realmente una lista de caracteres, o 99887766555443320 , así que lo descomponden como una lista.

  Arrays.sort(expect); Arrays.sort(search); 1  

El Arrays.sort(expect); Arrays.sort(search); 2 es el operador de la lista, Arrays.sort(expect); Arrays.sort(search); 3 es el cabezal de la lista y 99887776655443324 es la cola. Entonces, Arrays.sort(expect); Arrays.sort(search); 5 y Arrays.sort(expect); Arrays.sort(search); 6 . (Y Arrays.sort(expect); Arrays.sort(search); 7 es la función que usamos por lo general para obtener el elemento en el índice 0 en una lista).

Arrays.sort(expect); Arrays.sort(search); 8 no está incorrecto, pero la aplicación de función en Haskell no requiere ningún paréntesis. Los paréntesis son para agrupar, ya que la aplicación de la función tiene la mayor precedencia de cualquier operación.

  Arrays.sort(expect); Arrays.sort(search); 9  

Uso de una declaración search0 En el nivel superior de una función suele ser una indicación de que puede usar un Guardia . Los guardias son un poco como un 998877665554433311 .

  search2  

Cada guardia comienza con una tubería ( search33 ) y se evalúa a un valor 99887776655443334 . Se realiza el primer número de patrones, luego los guardias se evalúan en orden. search5565443335 es solo un sinónimo de search6 , es decir, un guardia que siempre tiene éxito cuando la evaluación lo alcanza.

Utilicé un patrón AS para unir el valor de search7 a un identificador. Este es un acceso directo útil y es mucho preferible a escribir search8 en todo el lugar si necesita tanto el valor original como su descomposición.

search9 es un poco extraño, y supongo que llega a esa respuesta cuando el compilador se quejó de que no sea una instancia para expect0 , ¡sí? La división de enteros utiliza una función llamada expect1 , expect22 es para expect3 valores (como expect44 o 99887776655443345 < / Código>) (Sí, es un poco extraño la primera vez que te encuentras con esto).

  expect6  

y expect7 tiene algunos paréntesis extra.

  expect8  

idiomático Haskell

Muchas de las operaciones que implementó se pueden expresar utilizando algunas de las funciones de orden superior que tenemos en el preludio y la codificación en un estilo funcional.

En lugar de usar la recursión primitiva en los elementos de una lista en expect9 , el uso típico de Haskell nos vería usando funciones como StringBuilder usedCharacters = new StringBuilder(); int searchPos = 0; while (searchPos < search.length) { int expectPos = Arrays.binarySearch(expect, search[searchPos]); if (expectPos < 0) { usedCharacters.append(search[searchPos]); } // advance to the next character, may be duplicates. searchPos++; while (searchPos < search.length && search[searchPos - 1] == search[searchPos]) { searchPos++; } } return usedCharacters.toString(); 0 y StringBuilder usedCharacters = new StringBuilder(); int searchPos = 0; while (searchPos < search.length) { int expectPos = Arrays.binarySearch(expect, search[searchPos]); if (expectPos < 0) { usedCharacters.append(search[searchPos]); } // advance to the next character, may be duplicates. searchPos++; while (searchPos < search.length && search[searchPos - 1] == search[searchPos]) { searchPos++; } } return usedCharacters.toString(); 1 Para manipular toda la lista sin meterse en las malezas.

  StringBuilder usedCharacters = new StringBuilder(); int searchPos = 0; while (searchPos < search.length) {     int expectPos = Arrays.binarySearch(expect, search[searchPos]);     if (expectPos < 0) {         usedCharacters.append(search[searchPos]);     }     // advance to the next character, may be duplicates.     searchPos++;     while (searchPos < search.length && search[searchPos - 1] == search[searchPos]) {         searchPos++;     } }  return usedCharacters.toString(); 2  

StringBuilder usedCharacters = new StringBuilder(); int searchPos = 0; while (searchPos < search.length) { int expectPos = Arrays.binarySearch(expect, search[searchPos]); if (expectPos < 0) { usedCharacters.append(search[searchPos]); } // advance to the next character, may be duplicates. searchPos++; while (searchPos < search.length && search[searchPos - 1] == search[searchPos]) { searchPos++; } } return usedCharacters.toString(); 3 s StringBuilder usedCharacters = new StringBuilder(); int searchPos = 0; while (searchPos < search.length) { int expectPos = Arrays.binarySearch(expect, search[searchPos]); if (expectPos < 0) { usedCharacters.append(search[searchPos]); } // advance to the next character, may be duplicates. searchPos++; while (searchPos < search.length && search[searchPos - 1] == search[searchPos]) { searchPos++; } } return usedCharacters.toString(); 4 , es decir, toma una función, que en sí mismo toma un elemento de tipo StringBuilder usedCharacters = new StringBuilder(); int searchPos = 0; while (searchPos < search.length) { int expectPos = Arrays.binarySearch(expect, search[searchPos]); if (expectPos < 0) { usedCharacters.append(search[searchPos]); } // advance to the next character, may be duplicates. searchPos++; while (searchPos < search.length && search[searchPos - 1] == search[searchPos]) { searchPos++; } } return usedCharacters.toString(); 5 y devuelve algo de Tipo StringBuilder usedCharacters = new StringBuilder(); int searchPos = 0; while (searchPos < search.length) { int expectPos = Arrays.binarySearch(expect, search[searchPos]); if (expectPos < 0) { usedCharacters.append(search[searchPos]); } // advance to the next character, may be duplicates. searchPos++; while (searchPos < search.length && search[searchPos - 1] == search[searchPos]) { searchPos++; } } return usedCharacters.toString(); 6 - y una lista y devuelve una lista donde esa función se ha aplicado a cada elemento de la primera lista. StringBuilder usedCharacters = new StringBuilder(); int searchPos = 0; while (searchPos < search.length) { int expectPos = Arrays.binarySearch(expect, search[searchPos]); if (expectPos < 0) { usedCharacters.append(search[searchPos]); } // advance to the next character, may be duplicates. searchPos++; while (searchPos < search.length && search[searchPos - 1] == search[searchPos]) { searchPos++; } } return usedCharacters.toString(); 7 Agrega todos los valores en una lista de números.

Una cosa adicional que podríamos hacer a esa función es escribirla en PointFree estilo. Funciones de escritura PointFree es definitivamente un aspecto del estilo Haskell, pero no se concentre en él sobre ello hasta que tenga un agarre sólido en lo básico del idioma. He incluido un Apéndice A en la parte inferior de este post, donde las funciones se han escrito de PointFree para su referencia.

StringBuilder usedCharacters = new StringBuilder(); int searchPos = 0; while (searchPos < search.length) { int expectPos = Arrays.binarySearch(expect, search[searchPos]); if (expectPos < 0) { usedCharacters.append(search[searchPos]); } // advance to the next character, may be duplicates. searchPos++; while (searchPos < search.length && search[searchPos - 1] == search[searchPos]) { searchPos++; } } return usedCharacters.toString(); 8 podría ser escrito muchas, muchas maneras (algunas como en los apéndices a continuación), pero dada el contexto de StringBuilder usedCharacters = new StringBuilder(); int searchPos = 0; while (searchPos < search.length) { int expectPos = Arrays.binarySearch(expect, search[searchPos]); if (expectPos < 0) { usedCharacters.append(search[searchPos]); } // advance to the next character, may be duplicates. searchPos++; while (searchPos < search.length && search[searchPos - 1] == search[searchPos]) { searchPos++; } } return usedCharacters.toString(); 9 , String usedCharacters = ""; ... in loop usedCharacters += c; 060 , y La especificación del algoritmo I Favorecería separar la funcionalidad en dos fases. Primero, construyendo una nueva cadena basada en duplicar todos los demás dígitos, y sumando en segundo lugar todos los dígitos. Para hacer esto introduciré una nueva función.

  String usedCharacters = ""; ... in loop     usedCharacters += c; 1  

y luego String usedCharacters = ""; ... in loop usedCharacters += c; 2 es una composición de String usedCharacters = ""; ... in loop usedCharacters += c; 3 y 99887766555443364 .

  String usedCharacters = ""; ... in loop     usedCharacters += c; 5  

99887766555443366 realmente podría beneficiarse de alguna aritmética modular. Utilice la función String usedCharacters = ""; ... in loop usedCharacters += c; 755443367 .

  String usedCharacters = ""; ... in loop     usedCharacters += c; 8  

Lo llevaré un poco más con el enlinado en los apéndices, pero como se representa, esta es una traducción muy legible del problema como se indicó. Tener una corregencia cercana al dominio problemático puede ser mucho más valioso que el código de TERSE por el bien de la terresia.

Apéndice A: PointFree

Una de las cosas que me gusta sobre el estilo de PointFree es que le obliga a pensar en términos de funciones, abstracciones de orden superior y tuberías de datos. Presentado sin más comentarios.

  String usedCharacters = ""; ... in loop     usedCharacters += c; 9  

Apéndice B: Código Golf

Comprender Esto lo ayudará a explorar el preludio y, con suerte, tener una comprensión más fuerte de la composición de la función. Aunque admito que es un poco llamativo. -))

  String c = Character.toString(s2.charAt(i)); ... ....contains(c) 0  

 

This is a good start! I'll do two passes over your code, one to address issues of style, and another to leverage functions from the Haskell Prelude to decrease code length and bring it more in line with typical Haskell usage and idioms.

Style

Your sumOfChars function is a good example of tail-recursion, but in Haskell we would take that String apart through pattern matching. A Haskell String is really a list of characters, or [Char], so we'll decompose it as a list.

sumOfChars :: [Char] -> Int -- String is a synonym for [Char] sumOfChars [] = 0 -- The empty list is the same value as the empty string, i.e. [] == "" sumOfChars (c:cs) = digitToInt c + sumOfChars cs 

The : is the list cons operator, c is the head of the list and cs is the tail. So, c == str !! 0 and cs == tail str. (And head is the function we usually use for getting the element at index 0 in a list.)

twoMult isn't wrong, but function application in Haskell doesn't require any parentheses. Parentheses are for grouping since function application has the highest precedence of any operation.

twoMult :: Char -> String twoMult c = show (digitToInt c * 2) 

Using an if statement at the top level of a function is usually an indication that you can use a guard. Guards are a bit like a multi-way if.

identificationSum :: [Char] -> Int identificationSum [] = 0 identificationSum s@(c:cs) -- The 's@' part is an as-pattern, explained below     | length s `mod` 2 == 1 = sumOfChars (twoMult c) + identificationSum cs     | otherwise             = digitToInt c + identificationSum cs 

Each guard begins with a pipe (|) and evaluates to a Bool value. First pattern matching takes place, then guards are evaluated in order. otherwise is just a synonym for True, i.e., a guard that always succeeds when evaluation reaches it.

I used an as-pattern there to bind the value of c:cs to an identifier. This is a handy shortcut and much preferable to writing c:cs all over the place if you need both the original value and its decomposition.

remainingToTens is a little odd, and I'd guess that you got to that answer when the compiler complained about there not being an instance for Fractional Int, yeah? Integer division uses a function called div, / is for Fractional values (like Float or Double) (yeah it's a little strange the first time you come across this).

remainingToTens :: Int -> Int remainingToTens x = (x `div` 10 + 1) * 10 - x -- Edit: This is incorrect, use version below 

And determineLastDigit just has a few extra parentheses.

determineLastDigit :: String -> Int determineLastDigit s = remainingToTens (identificationSum s) 

Idiomatic Haskell

Many of the operations you implemented can be expressed using some of the higher-order functions we have in the Prelude and coding in a functional style.

Instead of using primitive recursion on the elements of a list in sumOfChars, typical Haskell usage would see us using functions like sum and map to manipulate the entire list without getting into the weeds.

sumOfChars :: [Char] -> Int sumOfChars cs = sum (map digitToInt cs) 

maps type is (a -> b) -> [a] -> [b], that is, it takes a function - that itself takes an element of type a and returns something of type b - and a list and returns a list where that function has been applied to every element of the first list. sum adds all the values in a list of numbers.

One further thing we could do to that function is to write it in pointfree style. Writing functions pointfree is definitely an aspect of Haskell style, but don't concentrate on it overmuch until you have a solid grasp on the basics of the language. I've included an Appendix A at the bottom of this post where functions have been written pointfree for your reference.

identificationSum could be written many, many ways (some as in the appendices below) but given the context of sumOfChars, twoMult, and the specification of the algorithm I would favor separating out the functionality into two phases. First, constructing a new string based on doubling every other digit, and secondly summing all of the digits. To do this I'll introduce a new function.

doubleAlternating :: [Char] -> [Char] doubleAlternating []       = [] doubleAlternating (c:[])   = twoMult c doubleAlternating (c:d:cs) = twoMult c ++ [d] ++ doubleAlternating cs 

And then identificationSum is a composition of doubleAlternating and sumOfChars.

identificationSum :: String -> Int identificationSum s = sumOfChars (doubleAlternating s) 

remainingToTens could really just benefit from some modular arithmetic. Use the rem function.

remainingToTens :: Int -> Int remainingToTens x = negate x `mod` 10 

I'll take it a little further with inlining in the appendices, but as it stands this is a very readable translation of the problem as it was stated. Having a close correspondence to the problem domain can be much more valuable than terse code for the sake of terseness.

Appendix A: Pointfree

One of the things I like about pointfree style is that it forces you to think in terms of functions, higher order abstractions, and data pipelines. Presented without further comment.

sumOfChars :: [Char] -> Int sumOfChars = sum . map digitToInt  twoMult :: Char -> String twoMult = show . (* 2) . digitToInt  identificationSum :: String -> Int identificationSum = sumOfChars . doubleAlternating  determineLastDigit :: String -> Int determineLastDigit = remainingToTens . identificationSum 

Appendix B: Code golf

Understanding this will help you explore the Prelude and hopefully have a stronger grasp of function composition. I'll admit it's a little showy though. ;-)

checksum :: String -> Int checksum s = negate (sum . map digitToInt . concatMap show . zipWith ($) (cycle [(* 2), id]) . map digitToInt $ s) `mod` 10 
 
 
   
   
9
 
vote

Problemas simples primero ...

  • Sugiero ser más explícito sobre lo que está importando. Por ejemplo,

      import Data.Char (digitToInt, intToDigit)    
  • remainingToTens x = ceiling(fromIntegral x / 10) * 10 - x podría ser más simplemente escrito como remainingToTens x = 10 - x `mod` 10 .
  • Sería bueno soportar '-' y '+' caracteres en la entrada.
  • Sería bueno fallar si la entrada contiene algún carácter que no sea un dígito decimal o un 9988777655544335 o un 9988777655544336 . En realidad, digitToInt también acepta 'a' a través de 'f' como dígitos hexadecimales, que desea rechazar.

Idiomatic Haskell

Como se mencionó @bisserlis, su uso de remainingToTens x = ceiling(fromIntegral x / 10) * 10 - x0 , 99887766555443311 , y los paréntesis no son idiomáticos en Haskell.

En general, las listas de Haskell pueden ser infinitamente largas. Por lo tanto, Haskell Idioms enfatiza la pereza, lo que implica el recorrido de la cabeza a la cola. Para obtener un código como este, construido para manejar cuerdas cortas, hace poca diferencia, pero debe intentar desarrollar un hábito de atravesar las listas de la cabeza a la cola, en un solo paso siempre que sea posible.

En ese espíritu, es mejor evitar las funciones de llamadas como remainingToTens x = ceiling(fromIntegral x / 10) * 10 - x2 , que atraviesa hasta el final de una lista. Especialmente no cuando lo hace dentro de una función recursiva remainingToTens x = ceiling(fromIntegral x / 10) * 10 - x3 . (Como resultado, sin embargo, 99887776655443314 podría ser necesario después de todo - ver más abajo.)

No me gusta el camino remainingToTens x = ceiling(fromIntegral x / 10) * 10 - x5 funciona. remainingToTens x = ceiling(fromIntegral x / 10) * 10 - x6 LLAMADAS remainingToTens x = ceiling(fromIntegral x / 10) * 10 - x7 Para rigir el doubo dígito, y 99887766555443318 lo convierte de nuevo en el reino numérico. Debe poder manejar el cálculo sin estricarse.

Las funciones de Helper, si solo están usadas por una función, deben ser alcanzadas. Puede usar una cláusula de 998877666655443319 99887776655544332020 .

Primera implementación sugerida

incorporando las ideas anteriores ...

  remainingToTens x = 10 - x `mod` 101  

He redefinido remainingToTens x = 10 - x `mod` 102 Para que devuelva el dígito como un remainingToTens x = 10 - x `mod` 103 en lugar de un remainingToTens x = 10 - x `mod` 104 . El usuario de sus funciones debe tratarse puramente con cadenas y caracteres, ya que las identificaciones personales suecas son cadenas, no enteros. (Los principales ceros y materia de puntuación, por ejemplo.)

Complicación

Aparentemente, las ID a veces se escriben con un año de cuatro dígitos en el campo Fecha de nacimiento. Sin embargo, el ejemplo de Wikipedia sueco ("19811218-9876") sugiere que incluso cuando se prepara el siglo, la suma de comprobación todavía se basa en la representación canónica utilizando un año de dos dígitos. Eso hace que la implementación sea más complicada: probablemente necesitaría reintroducir una llamada a remainingToTens x = 10 - x `mod` 105 (solo una llamada en un contexto no recursivo, la mente) o haga una cantidad loca de coincidencia de patrones.

  remainingToTens x = 10 - x `mod` 106  

Sugerencia alternativa API

Me imagino que el caso de uso común podría ser agregar un dígito de suma de suma de suma. ¿Quizás esta API alternativa podría ser más útil? Una función se ocupa de ambos remainingToTens x = 10 - x `mod` 107 y remainingToTens x = 10 - x `mod` 108

, y todas sus funciones de ayuda están cuidadosamente alcejadas en el interior.
  remainingToTens x = 10 - x `mod` 109  
 

Simple issues firstxe2x80xa6

  • I suggest being more explicit about what you are importing. For example,

    import Data.Char (digitToInt, intToDigit) 
  • remainingToTens x = ceiling(fromIntegral x / 10) * 10 - x could be more simply written as remainingToTens x = 10 - x `mod` 10.
  • It would be nice to support '-' and '+' characters in the input.
  • It would be good to fail if the input contains any character other than a decimal digit or a '-' or a '+'. Actually, digitToInt also accepts 'a' through 'f' as hexadecimal digits, which you want to reject.

Idiomatic Haskell

As @bisserlis mentioned, your use of !!, if-then-else, and parentheses are not idiomatic in Haskell.

In general, Haskell lists can be infinitely long. Therefore, Haskell idioms emphasize laziness, which implies head-to-tail traversal. For code like this, built to handle short strings, it makes little difference, but you should try to develop a habit of traversing lists from head to tail, in one single pass whenever possible.

In that spirit, it is best to avoid calling functions like length, which traverse all the way to the end of a list. Especially not when you do so within a recursive function identificationSum. (As it turns out, however, length might be necessary after all xe2x80x94 see below.)

I don't like the way sumOfChars(twoMult(str !! 0)) works. twoMult calls show to stringify the doubled digit, and sumOfChars immediately converts it back into the numeric realm. You should be able to handle the calculation without stringifying.

Helper functions, if they are only ever used by one function, should be scoped. You can use either a where clause or a let helper = xe2x80xa6 in xe2x80xa6 expression.

First suggested implementation

Incorporating the ideas abovexe2x80xa6

import Data.Char (digitToInt, intToDigit)  sumOfChars :: String -> Int sumOfChars str = sumOfChars' double id str   where     sumOfChars' f f' ""        = 0     sumOfChars' f f' (c:cs)       | c == '-' || c == '+' = sumOfChars' f f' cs       | '0' <= c && c <= '9' = (f (digitToInt c)) + sumOfChars' f' f cs     double n       | n < 5  = 2 * n       | n < 10 = 2 * n + (1 - 10)  lastDigit :: String -> Char lastDigit str = intToDigit $ 10 - (sumOfChars str) `mod` 10 

I've redefined lastDigit so that it returns the digit as a Char rather than an Int. The user of your functions should be dealing purely with strings and characters, as Swedish Personal IDs are strings, not integers. (Leading zeroes and punctuation matter, for example.)

Complication

Apparently, IDs are sometimes written with a four-digit year in the birthdate field. However, the Swedish Wikipedia example ("19811218-9876") suggests that even when the century is prepended, the checksum is still based on the canonical representation using a two-digit year. That makes implementation trickier: you would probably need to either reintroduce a call to length (just one call in a non-recursive context, mind you) or do a crazy amount of pattern matching.

sumOfChars :: String -> Int sumOfChars str@(_:_:cs)   | length str > 11 = sumOfChars' double id cs   | otherwise       = sumOfChars' double id str   where     sumOfChars' f f' ""        = 0     sumOfChars' f f' (c:cs)       | c == '-' || c == '+' = sumOfChars' f f' cs       | '0' <= c && c <= '9' = (f (digitToInt c)) + sumOfChars' f' f cs     double n       | n < 5  = 2 * n       | n < 10 = 2 * n + (1 - 10) 

Alternative API suggestion

I imagine that the common use-case might be to append a checksum digit. Perhaps this alternative API might be more useful? One function takes care of both sumChars and determineLastDigit, and all its helper functions are neatly scoped inside.

completeID :: String -> String  -- Support four-digit-year variant representation completeID (y1:y2:y3:y4:m1:m2:d1:d2:n1:n2:n3:[])     = y1:y2:completeID (y3:y4:m1:m2:d1:d2:n1:n2:n3:[]) completeID (y1:y2:y3:y4:m1:m2:d1:d2:'+':n1:n2:n3:[]) = y1:y2:completeID (y3:y4:m1:m2:d1:d2:'+':n1:n2:n3:[]) completeID (y1:y2:y3:y4:m1:m2:d1:d2:'-':n1:n2:n3:[]) = y1:y2:completeID (y3:y4:m1:m2:d1:d2:'-':n1:n2:n3:[])  completeID cs = completeID' double id 0 cs   where     completeID' f f' sum ""  = [intToDigit $ 10 - sum `mod` 10]     completeID' f f' sum (c:cs)       | c == '-' || c == '+' = c : completeID' f f' sum cs       | '0' <= c && c <= '9' = c : completeID' f' f (sum + f (digitToInt c)) cs     double n       | n < 5  = 2 * n       | n < 10 = 2 * n + (1 - 10) 
 
 

Relacionados problema

7  Generador de archivos de prueba binaria con suma de comprobación  ( Binary test file generator with checksum ) 
en la lectura Esta pregunta , se me ocurrió que sería útil crear archivos de prueba de acuerdo con la especificación del formato de archivo. Para recubrir, e...

1  Un algoritmo para calcular el dígito de verificación para códigos SSCC GS1 de 18 dígitos  ( An algorithm to calculate the check digit for 18 digit sscc gs1 codes ) 
Estoy calculando los códigos SSCC GS1 que son códigos de barras de 18 dígitos. El dígito número 18 es un dígito de verificación que se usa para validar el res...

21  Algoritmo de suma de comprobación personalizada  ( Custom checksum algorithm ) 
A MIENTROS RUENTANDO, INVERSO: diseñé un algoritmo de suma de comprobación de una MMO que se usa para comprobar el Validez de un artículo que está vinculado...

3  Parse el archivo de datos y verifique la suma de comprobación  ( Parse data file and verify checksum ) 
Aquí está mi tarea: Escriba un programa que realice un nombre de archivo como su único argumento de la línea de comandos e imprime los resultados en std::...

14  Compruebe la validez de un número de identificación de Sudáfrica usando el algoritmo Luhn  ( Check the validity of a south african id number using the luhn algorithm ) 
import java.math.BigInteger; /** * Created by lungisani on 2017/02/25. */ public class Luhn { public Boolean getIdentitySummation(BigInteger identitie...

4  Validador de "persona" sueco "  ( Swedish personnummer validator ) 
Tengo un pequeño programa C # que valida los números de Seguro Social sueco ( 'personnummer' < / a>). Elegí usar Rogex ya que hay muchas formas diferentes e...

5  Implementación del algoritmo Luhn  ( Implementation of luhn algorithm ) 
He implementado el conocido algoritmo Luhn en Python. Es simple, por lo que es bueno para los principiantes. static unsafe bool IsAllZeros(byte[] data) { ...

7  Google Foobar Xor CheckSum Challenge  ( Google foobar xor checksum challenge ) 
Google Foobar surgió hace unos días y lo tomé como un desafío para aprender a Python rápidamente mientras se divierte. Sin embargo, me encontré con un desafío...

6  A S.I.n Checker, usando el algoritmo de Luhn, y creado en swing  ( A s i n checker using luhns algorithm and created in swing ) 
He creado un programa para validar un canadiense S.I.N. Usando el algoritmo de Luhn, y se preguntaba si había alguna forma de hacerlo más eficiente. Por lo qu...

9  Código Haskell para verificar el número de crédito  ( Haskell code to verify credit number ) 
Soy nuevo en Haskell, y escribió un script para verificar el número de crédito. Hice algunas pruebas, trabajó el script, pero ¿puede mejorarse más? isCredi...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos