def check_number(str):
digits = [int(i) for i in reversed(str)]
double = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
ressum = 0
for i in xrange(0, len(digits), 2):
try:
ressum += digits + double[digits[i + 1]]
except:
ressum += digits
#print ressum # for debug
return not (ressum % 10)
print check_number("49927398716")
print check_number("49927398717")
Разминка: Luhn algorithm
Luhn.
И началось соревнование, кто напишет (на Python) функцию по-интереснее.
Предлагаю и вам попробовать. Свою версию выложу позднее.
Необходимо было применить в одной из задач алгоритм валидации номеров кредитных карт
И началось соревнование, кто напишет (на Python) функцию по-интереснее.
Предлагаю и вам попробовать. Свою версию выложу позднее.
Код:
Может и не лучше, чем в оригинале, но по другому. Список double можно было сгенерить из двух range, но так нагляднее.
[QUOTE]def checkluhn (lst):
[COLOR=#e1e4f2]def [/COLOR]return sum (map (lambda x: x[1] if (x[0] % 2) else (x[1]*2 > 9 and x[1]*2 - 9) or (x[1]*2), list (enumerate (reversed (lst), 1)))) % 10 == 0
print (checkluhn ([4, 5, 6, 1, 2, 6, 1, 2, 1, 2, 3, 4, 5, 4, 6, 7]))
>> True
Цитата: Ander Skirnir
Не шарю синтаксических хитростей, потому длинновато и legacy'евато.
Хмм, вникать в IronPython пока не стал, щас наверно разберусь, но вариант решения на Nemerle тут покажу, не пинайте, плз.
Код:
Luhn(data : string) : bool {
def f1(lst) {
| [] => 0
| d :: tail => d + f2(tail)
}
and f2(lst) {
| [] => 0
| d :: tail => (d2 => if (d2 > 9) d2 - 9 else d2) (d * 2) + f1(tail)
}
f1(NString.Explode(data ?? string.Empty)
.Map(c => if(char.IsDigit(c)) (c :> int - '0' :> int)
else throw ArgumentOutOfRangeException("data", $"Invalid character '$c'."))
.Reverse()) % 10 == 0
}
def f1(lst) {
| [] => 0
| d :: tail => d + f2(tail)
}
and f2(lst) {
| [] => 0
| d :: tail => (d2 => if (d2 > 9) d2 - 9 else d2) (d * 2) + f1(tail)
}
f1(NString.Explode(data ?? string.Empty)
.Map(c => if(char.IsDigit(c)) (c :> int - '0' :> int)
else throw ArgumentOutOfRangeException("data", $"Invalid character '$c'."))
.Reverse()) % 10 == 0
}
В версии от Kogrom мне не нравится try. Можно было бы его удалить, если дополнить анализируемый список нулями до четного размера.
В версии от Ander Skirnir мне не нравится повторяемое 3 раза выражение x[1]*2 (камень в сторону ФП), и то, что в списке могут быть многозначные числа. Вторую проблему в оригинале решают с помощью исключения, а Kogrom с помощью обоснованного чита.
Хотелось бы как раз видеть реализации с уклоном в сторону ФП, т.к. там для данной задачи больше возможеностей для креатива.
Цитата: Green
Хотелось бы как раз видеть реализации с уклоном в сторону ФП, т.к. там для данной задачи больше возможеностей для креатива.
У меня еще есть два варианта (Остапа понесло...).
#2. Избавляемся вот Reverse с помощью FoldRight:
Код:
Luhn2(data : string) : bool {
def res = NString.Explode(data ?? string.Empty)
.FoldRight((false, 0), fun(c, acc) {
def d = if(char.IsDigit(c)) (c :> int - '0' :> int)
else throw ArgumentOutOfRangeException("data", $"Invalid character '$c'.");
match(acc) {
| (false, sum) => (true, sum + d)
| (true, sum) => (false, sum + (d2 => if (d2 > 9) d2 - 9 else d2) (d * 2))
}
});
match(res) {
| (_, sum) => sum % 10 == 0
}
}
def res = NString.Explode(data ?? string.Empty)
.FoldRight((false, 0), fun(c, acc) {
def d = if(char.IsDigit(c)) (c :> int - '0' :> int)
else throw ArgumentOutOfRangeException("data", $"Invalid character '$c'.");
match(acc) {
| (false, sum) => (true, sum + d)
| (true, sum) => (false, sum + (d2 => if (d2 > 9) d2 - 9 else d2) (d * 2))
}
});
match(res) {
| (_, sum) => sum % 10 == 0
}
}
Код:
Luhn3(data : string) : bool {
def fold(seq) {
if(seq.MoveNext()) {
def c = seq.Current;
def d = if(char.IsDigit(c)) (c :> int - '0' :> int)
else throw ArgumentOutOfRangeException("data", $"Invalid character '$c'.");
match(fold(seq)) {
| (false, sum) => (true, sum + d)
| (true, sum) => (false, sum + (d2 => if (d2 > 9) d2 - 9 else d2) (d * 2))
}
} else (false, 0)
}
match(fold((data ?? string.Empty).GetEnumerator())) {
| (_, sum) => sum % 10 == 0
}
}
def fold(seq) {
if(seq.MoveNext()) {
def c = seq.Current;
def d = if(char.IsDigit(c)) (c :> int - '0' :> int)
else throw ArgumentOutOfRangeException("data", $"Invalid character '$c'.");
match(fold(seq)) {
| (false, sum) => (true, sum + d)
| (true, sum) => (false, sum + (d2 => if (d2 > 9) d2 - 9 else d2) (d * 2))
}
} else (false, 0)
}
match(fold((data ?? string.Empty).GetEnumerator())) {
| (_, sum) => sum % 10 == 0
}
}
Код:
def check_number(luhnstr):
assert luhnstr
double = lambda x: [0, 2, 4, 6, 8, 1, 3, 5, 7, 9][x]
def Calc(luhnstr = luhnstr, ressum = 0, func = double):
if not luhnstr: return ressum
else:
func = (lambda x: x) if func == double else double
return Calc(luhnstr[:-1], ressum + func(int(luhnstr[-1])), func)
#print Calc() # for debug
return not (Calc() % 10)
print check_number("49927398716")
print check_number("49927398717")
print check_number("")
assert luhnstr
double = lambda x: [0, 2, 4, 6, 8, 1, 3, 5, 7, 9][x]
def Calc(luhnstr = luhnstr, ressum = 0, func = double):
if not luhnstr: return ressum
else:
func = (lambda x: x) if func == double else double
return Calc(luhnstr[:-1], ressum + func(int(luhnstr[-1])), func)
#print Calc() # for debug
return not (Calc() % 10)
print check_number("49927398716")
print check_number("49927398717")
print check_number("")
Код:
from itertools import cycle
def check_number(luhnstr):
assert luhnstr
myiter = cycle([lambda x: x, lambda x: [0, 2, 4, 6, 8, 1, 3, 5, 7, 9][x]])
return not (sum([myiter.next()(int(i)) for i in reversed(luhnstr)]) % 10)
print check_number("49927398716")
print check_number("49927398717")
print check_number("")
def check_number(luhnstr):
assert luhnstr
myiter = cycle([lambda x: x, lambda x: [0, 2, 4, 6, 8, 1, 3, 5, 7, 9][x]])
return not (sum([myiter.next()(int(i)) for i in reversed(luhnstr)]) % 10)
print check_number("49927398716")
print check_number("49927398717")
print check_number("")
Но потом избавился.
Цитата: Green
У меня тоже был cycle: cycle((1,2))
Но потом избавился.
Но потом избавился.
Можно. Как-то так:
Код:
def check_number(luhnstr):
assert luhnstr
funcs = (lambda x: [0, 2, 4, 6, 8, 1, 3, 5, 7, 9][x], lambda x: x)
def Calc(i = -1, ressum = 0):
if i < -len(luhnstr): return ressum
else: return Calc(i - 1, ressum + funcs[i % 2](int(luhnstr)))
return not (Calc() % 10)
assert luhnstr
funcs = (lambda x: [0, 2, 4, 6, 8, 1, 3, 5, 7, 9][x], lambda x: x)
def Calc(i = -1, ressum = 0):
if i < -len(luhnstr): return ressum
else: return Calc(i - 1, ressum + funcs[i % 2](int(luhnstr)))
return not (Calc() % 10)
#4. Комбинация #1 и #3.
Код:
Luhn4(data : string) : bool {
def to_digit(c) {
if(char.IsDigit(c)) (c :> int - '0' :> int)
else throw ArgumentOutOfRangeException("data", $"Invalid character '$c'.");
}
def f1(seq) {
if(seq.MoveNext()) {
to_digit(seq.Current) + f2(seq)
} else 0
}
and f2(seq) {
if(seq.MoveNext()) {
def d2 = to_digit(seq.Current) * 2;
(if (d2 > 9) d2 - 9 else d2) + f1(seq)
} else 0
}
def input = data ?? string.Empty;
def seq = input.GetEnumerator();
(if(input.Length % 2 == 0) f2(seq) else f1(seq)) % 10 == 0
}
def to_digit(c) {
if(char.IsDigit(c)) (c :> int - '0' :> int)
else throw ArgumentOutOfRangeException("data", $"Invalid character '$c'.");
}
def f1(seq) {
if(seq.MoveNext()) {
to_digit(seq.Current) + f2(seq)
} else 0
}
and f2(seq) {
if(seq.MoveNext()) {
def d2 = to_digit(seq.Current) * 2;
(if (d2 > 9) d2 - 9 else d2) + f1(seq)
} else 0
}
def input = data ?? string.Empty;
def seq = input.GetEnumerator();
(if(input.Length % 2 == 0) f2(seq) else f1(seq)) % 10 == 0
}
#5.
Код:
Luhn5(data : string) : bool {
def to_digit(c) {
if(char.IsDigit(c)) (c :> int - '0' :> int)
else throw ArgumentOutOfRangeException("data", $"Invalid character '$c'.");
}
def next(seq, s) {
if(seq.MoveNext())
s(seq, to_digit(seq.Current))
else 0
}
def s1(seq, d) {
next(seq, s2) + d
}
and s2(seq, d) {
def d2 = d * 2;
next(seq, s1) + if (d2 > 9) d2 - 9 else d2
}
def input = data ?? string.Empty;
next(input.GetEnumerator(), if(input.Length % 2 == 0) s2 else s1) % 10 == 0
}
def to_digit(c) {
if(char.IsDigit(c)) (c :> int - '0' :> int)
else throw ArgumentOutOfRangeException("data", $"Invalid character '$c'.");
}
def next(seq, s) {
if(seq.MoveNext())
s(seq, to_digit(seq.Current))
else 0
}
def s1(seq, d) {
next(seq, s2) + d
}
and s2(seq, d) {
def d2 = d * 2;
next(seq, s1) + if (d2 > 9) d2 - 9 else d2
}
def input = data ?? string.Empty;
next(input.GetEnumerator(), if(input.Length % 2 == 0) s2 else s1) % 10 == 0
}
[highlight=haskell]
import Data.Char
conv x = (x-(div x 10)*9)
crcLuhn a [] = a
crcLuhn a (x:[]) = a + x
crcLuhn a (x1:x2:xt) = crcLuhn (a + x1 + conv (x2*2)) xt
checkLuhn x = (mod p 10 == 0) where l = reverse $ map digitToInt x
p = crcLuhn 0 l[/highlight]
Цитата:
def checkluhn (str):
[COLOR=#e1e4f2]def [/COLOR]return sum (map (lambda x: (lambda t: x[1] if (x[0] % 2) else (t > 9 and t - 9) or t)(*[x[1]*2]), list (enumerate (reversed (list (map (int, list (str)))), 1)))) % 10 == 0
[COLOR=#e1e4f2]def [/COLOR]return sum (map (lambda x: (lambda t: x[1] if (x[0] % 2) else (t > 9 and t - 9) or t)(*[x[1]*2]), list (enumerate (reversed (list (map (int, list (str)))), 1)))) % 10 == 0
И простое, каноническое рекурсивное решение (:
Цитата:
def fp_checkluhn (str):
[COLOR=#e1e4f2]def [/COLOR]def rec_checkluhn (str, k = 0):
[COLOR=#e1e4f2]def def [/COLOR]if str == '':
[COLOR=#e1e4f2]def def def [/COLOR]return 0
[COLOR=#e1e4f2]def def [/COLOR]t = int (str[-1]) * (1 + k)
[COLOR=#e1e4f2]def def [/COLOR]return (t - 9 * (t // 10) + rec_checkluhn (str[:-1], 1 - k)) % 10
[COLOR=#e1e4f2]def [/COLOR]return rec_checkluhn (str) == 0
[COLOR=#e1e4f2]def [/COLOR]def rec_checkluhn (str, k = 0):
[COLOR=#e1e4f2]def def [/COLOR]if str == '':
[COLOR=#e1e4f2]def def def [/COLOR]return 0
[COLOR=#e1e4f2]def def [/COLOR]t = int (str[-1]) * (1 + k)
[COLOR=#e1e4f2]def def [/COLOR]return (t - 9 * (t // 10) + rec_checkluhn (str[:-1], 1 - k)) % 10
[COLOR=#e1e4f2]def [/COLOR]return rec_checkluhn (str) == 0
Лаконизированный вариант:
Цитата:
def tiny_checkluhn (str):
[COLOR=#e1e4f2]def [/COLOR]def rec (str, k = 0):
[COLOR=#e1e4f2]def def [/COLOR]return 0 if str == '' else (lambda t: (t - 9 * (t // 10) + rec (str[:-1], 1 - k)) % 10) (int (str[-1]) * (1 + k))
[COLOR=#e1e4f2]def [/COLOR]return rec (str) % 10 == 0
[COLOR=#e1e4f2]def [/COLOR]def rec (str, k = 0):
[COLOR=#e1e4f2]def def [/COLOR]return 0 if str == '' else (lambda t: (t - 9 * (t // 10) + rec (str[:-1], 1 - k)) % 10) (int (str[-1]) * (1 + k))
[COLOR=#e1e4f2]def [/COLOR]return rec (str) % 10 == 0
x // 5 - позаимствовано из поста ниже :)
Код:
def check_number(luhnstr):
assert luhnstr
funcs = (lambda x: x, lambda x: 2 * x - 9 * (x // 5))
return sum([funcs[i % 2](int(a)) for i, a in enumerate(reversed(luhnstr))]) % 10 == 0
assert luhnstr
funcs = (lambda x: x, lambda x: 2 * x - 9 * (x // 5))
return sum([funcs[i % 2](int(a)) for i, a in enumerate(reversed(luhnstr))]) % 10 == 0
Код:
def luhn(card):
return not (lambda d: sum(d[::2]) + reduce(lambda s,t: s+sum(divmod(t*2,10)), d[1::2], 0))(map(int, reversed(card))) % 10
print luhn('371449635398431')
print luhn('371449635398432')
return not (lambda d: sum(d[::2]) + reduce(lambda s,t: s+sum(divmod(t*2,10)), d[1::2], 0))(map(int, reversed(card))) % 10
print luhn('371449635398431')
print luhn('371449635398432')
Цитата: Green
Показываю своё:
Скрадываю и упрощаю:
Код:
def check_number(card):
#return not (lambda d: sum(d[::2]) + reduce(lambda s,t: s+sum(divmod(t*2,10)), d[1::2], 0))(map(int, reversed(card))) % 10
return not (lambda d: sum(d[-1::-2]) + sum([sum(divmod(t*2,10)) for t in d[-2::-2]]))(map(int, card)) % 10#
print check_number("49927398716")
print check_number("49927398717")
print check_number('371449635398431')
print check_number('371449635398432')
print check_number("") # oops!
#return not (lambda d: sum(d[::2]) + reduce(lambda s,t: s+sum(divmod(t*2,10)), d[1::2], 0))(map(int, reversed(card))) % 10
return not (lambda d: sum(d[-1::-2]) + sum([sum(divmod(t*2,10)) for t in d[-2::-2]]))(map(int, card)) % 10#
print check_number("49927398716")
print check_number("49927398717")
print check_number('371449635398431')
print check_number('371449635398432')
print check_number("") # oops!
и это, на пустую строку в этих версиях вернет True.
а удаление reversed принимается
пустые строки - это все же не соль