Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Разминка: Luhn algorithm

3
08 октября 2009 года
Green
4.8K / / 20.01.2000
Необходимо было применить в одной из задач алгоритм валидации номеров кредитных карт Luhn.
И началось соревнование, кто напишет (на Python) функцию по-интереснее.
Предлагаю и вам попробовать. Свою версию выложу позднее.
87
09 октября 2009 года
Kogrom
2.7K / / 02.02.2008
Первое, что пришло в голову:

Код:
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")

Может и не лучше, чем в оригинале, но по другому. Список double можно было сгенерить из двух range, но так нагляднее.
29K
09 октября 2009 года
Ander Skirnir
109 / / 08.06.2009
- в последнем оно со скроллингом выходит.

[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

5
09 октября 2009 года
hardcase
4.5K / / 09.08.2005
Цитата: 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
}
Использовать как Luhn("49927398716").
87
09 октября 2009 года
Kogrom
2.7K / / 02.02.2008
Буду критиковать.

В версии от Kogrom мне не нравится try. Можно было бы его удалить, если дополнить анализируемый список нулями до четного размера.

В версии от Ander Skirnir мне не нравится повторяемое 3 раза выражение x[1]*2 (камень в сторону ФП), и то, что в списке могут быть многозначные числа. Вторую проблему в оригинале решают с помощью исключения, а Kogrom с помощью обоснованного чита.
3
09 октября 2009 года
Green
4.8K / / 20.01.2000
Хотелось бы как раз видеть реализации с уклоном в сторону ФП, т.к. там для данной задачи больше возможеностей для креатива.
5
09 октября 2009 года
hardcase
4.5K / / 09.08.2005
Цитата: 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
    }
}
#3. Избавляемся от списков вообще (IEnumerable<char> нам хватит), алгоритм тот же что и в #2:
Код:
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
    }
}
Нет модифицируемых переменных, нет циклов.
87
09 октября 2009 года
Kogrom
2.7K / / 02.02.2008
Тоже от реверса избавился. В остальном почти то же самое:
Код:
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("")
87
09 октября 2009 года
Kogrom
2.7K / / 02.02.2008
Теперь почти в стиле Ander Skirnir:

 
Код:
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("")
3
09 октября 2009 года
Green
4.8K / / 20.01.2000
У меня тоже был cycle: cycle((1,2))
Но потом избавился.
87
09 октября 2009 года
Kogrom
2.7K / / 02.02.2008
Цитата: 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)
5
09 октября 2009 года
hardcase
4.5K / / 09.08.2005
А я после обеда еще один вариант родил.:)
#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
}
5
09 октября 2009 года
hardcase
4.5K / / 09.08.2005
Гулять - так гулять. Слегка допиленный #4 (итератор локализован в одной функции), пожалуй на этом остановлюсь.
#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
}
361
09 октября 2009 года
Odissey_
661 / / 19.09.2006
Мое решение на Haskell, вроде народ в irc-е не против решения не на Python

[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]
29K
09 октября 2009 года
Ander Skirnir
109 / / 08.06.2009
Допиленный до полного неадеквата перлизм:
Цитата:
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



И простое, каноническое рекурсивное решение (:

Цитата:
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



Лаконизированный вариант:

Цитата:
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



x // 5 - позаимствовано из поста ниже :)

87
09 октября 2009 года
Kogrom
2.7K / / 02.02.2008
Потырил у всех:

 
Код:
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
3
09 октября 2009 года
Green
4.8K / / 20.01.2000
Показываю своё:
 
Код:
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')
87
09 октября 2009 года
Kogrom
2.7K / / 02.02.2008
Цитата: 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!

и это, на пустую строку в этих версиях вернет True.
3
09 октября 2009 года
Green
4.8K / / 20.01.2000
for не использовал преднамерянно, IMHO как-то не функционально...
а удаление reversed принимается

пустые строки - это все же не соль
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог