Что использовать вместо понимания списка - PullRequest
5 голосов
/ 26 января 2011

Я только начал работать с Хаскеллом и закончил хорошее упражнение , чтобы создать шифр Цезаря.

Одним из первых шагов было создание функции, которая будет принимать букву и превращать ее в число. Я знаю, что chr и ord могут сделать это уже, но частью упражнения было написать свой собственный.

let2num c = head [ b | (a,b) <- zip ['a'..'z'] [0..25], a==c]

Я новичок в синтаксисе Haskell, и одним из первых, что я узнал, было понимание списков, и это стало моим молотом. Мне очень любопытно, как еще (возможно, лучше) написать эту функцию?

Если вам интересно, остальная часть шифра находится в гисте .

EDIT

Мне также интересны другие способы перевода обратно с цифр на буквы.

num2let d = head [ a | (a,b) <- zip ['a'..'z'] [0..25], b==(d `mod` 26)]

Ответы [ 6 ]

4 голосов
/ 26 января 2011

Обратный процесс:

num2let = (!!) ['a'..'z']

!! - это оператор индекса списка (индекс) , начиная с 0.Это экземпляр более общего Data.List.genericIndex, который принимает индекс любого целочисленного типа.

(!!) здесь частично применен , что означает, что ему все еще нужен один аргумент типаInt для получения результата (значение из списка, индекс которого равен Int значению, которое вы передаете num2let).

4 голосов
/ 26 января 2011

Мое решение:

import Data.List
let2num c = let (Just n) = elemIndex c ['a'..'z'] in n

Или:

import Data.List
import Data.Maybe
let2num c = fromJust $ elemIndex c ['a'..'z']

Или в бессмысленном стиле :

import Data.List
import Data.Maybe
let2num = fromJust . (flip elemIndex) ['a'..'z']

Функция elemIndex возвращает индекс первого элемента в данном списке, равный (==) элементу запроса, или Nothing, если такого элемента нет.

Тип Maybe инкапсулируетнеобязательное значение.Значение типа Maybe a либо содержит значение типа a (представленное как Just a), либо оно пустое (представлено как Nothing).Использование Maybe - хороший способ справиться с ошибками или исключительными случаями, не прибегая к таким радикальным мерам, как ошибка.

Функция fromJust извлекает элемент из Just.

3 голосов
/ 26 января 2011

«Цезарь просто заменил каждую букву в сообщении на букву в трех местах дальше по алфавиту, заключив ее в конец алфавита». Мы можем просто написать это на Хаскеле.Фактически мы можем избежать let2num и num2let в целом.

Итак, давайте начнем с определения таблицы для сопоставления простого текстового алфавита с алфавитом зашифрованного текста:

cipher = let abc = ['a'..'z']
             code = drop 3 abc ++ take 3 abc
         in  zip abc code

Это будет выглядетькак

[('a','d'),('b','e'),('c','f'),('d','g'), ... ]

Теперь мы можем зашифровать символ, если просто lookup буква в этом словаре:

ghci> lookup 'a' cipher
Just 'd'

lookup возвращает значение Maybe Char, нам нужночтобы преобразовать его просто в Char, и для этого я использую функцию maybe, используя '?' для символов, которые не были найдены в шифре, и id (функция идентификации = без изменений) для найденных символов:

ghci> maybe '?' id (lookup 'a' cipher)
'd'

Теперь мы можем написать функцию encrypt для кодирования только одного символа, оставляя пропущенные символы, например пробел, незашифрованными:

encrypt c = maybe c id (lookup c cipher)

Для шифрования всей строки:

ghci> map encrypt "haskell is fun"
"kdvnhoo lv ixq"

Итак, мы можем собрать все вместе:

encrypt c = maybe c id (lookup c cipher)
  where
  cipher = let abc = ['a'..'z']
               code = drop 3 abc ++ take 3 abc
           in  zip abc code
2 голосов
/ 27 января 2011

Для полноты, я думаю, что кто-то должен упомянуть, что списочные выражения - это просто ярлык для написания материала в монаде списка.Ваш расшифрованный код примерно такой:

let2num c = head $ do (a,b) <- zip ['a'..'z'] [0..25]
                      if a == c then [b] else []

Не очень интересный пример, но вы идете.

Кроме того, десагоринг синтаксиса do, это то же самое:

let2num c = head $ zip ['a'..'z'] [0..25] >>= \(a,b) -> if a == c then [b] else []
0 голосов
/ 28 января 2011

Я бы сказал следующее:

import Data.Char

caesar :: Int -> Char -> Char
caesar n c = if isAlpha c 
             then chr (ord 'a' + (ord c - ord 'a' + n) `mod` 26) 
             else c

и map (caesar n) над строкой с n желаемым смещением.

0 голосов
/ 26 января 2011

Я не уверен, почему вы против решения ord.Решения на основе списка выполняют ненужную работу (обход списка).И они все еще обращаются к вызову enumFromTo, метода класса Enum, который позволяет конвертировать между Int с и Char с так же, как ord / chr.Это интерфейс самого низкого уровня, предусмотренный для типа Char, поэтому вы вряд ли сможете «написать свое» здесь (не считая самостоятельную упаковку / распаковку, но это не большая радость).

...