Правильный способ форматирования Haskell функций с учетом области применения? - PullRequest
1 голос
/ 28 марта 2020

Я новичок в Haskell. Я собрал базовый c Цезарь Шифр, он работает, но он очень грязный и трудный для чтения.

caesarCipher :: Int -> String -> String
caesarCipher n xs = [shift n x | x <- xs] 
shift n c  = num2let ((let2num c + n) `mod` 26)
alphabet = ['a'..'z']
let2num c = head[ b | (a,b) <- zip alphabet [0..length alphabet], a==c]
num2let = (!!) alphabet

Каков «правильный» способ Haskell форматировать функции, которые состоят нескольких переменных и выражений, и я должен рассмотреть область переменных? И кроме предположений, основанных на эффективности, я допустил какие-либо другие "серьезные" ошибки?

Это моя попытка:

caesarCipher n xs = let
    shift n c  = num2let ((let2num c + n) `mod` 26) where
        alphabet = ['a'..'z']
        let2num c = head[ b | (a,b) <- zip alphabet [0..length alphabet], a==c]
        num2let = (!!) alphabet
    in [shift n x | x <- xs]

1 Ответ

3 голосов
/ 28 марта 2020

Я бы прежде всего переписал некоторые функции. Например. zip alphabet [0 .. length alphabet] можно заменить на zip alphabet [0..], поскольку zip остановится с того момента, как один из списков будет исчерпан. Использование (!!) и head часто не является хорошей практикой, поскольку эти функции не являются полными: если индекс слишком велик или список пуст, (!!) и head приведут к ошибке соответственно.

Мы можем определить вспомогательные функции, например, для num2let:

import Data.Char(chr, ord)

num2let :: Int -> Char
num2let n = chr (n + ord 'a')

здесь num2let отобразит 0 в 'a', 1 в 'b' и др. c.

let2num можно сделать аналогичным образом:

import Data.Char(ord)

let2num :: Char -> Int
let2num c = ord c - ord 'a'

Так что теперь мы можем определить caesarCipher как:

caesarCipher :: Int -> String -> String
caesarCipher n = map (num2let . (`mod 26`) . (n+) . let2num)

Так что будет выглядеть так:

import Data.Char(chr, ord)

num2let :: Int -> Char
num2let n = chr (n + ord 'a')

let2num :: Char -> Int
let2num c = ord c - ord 'a'

caesarCipher :: Int -> String -> String
caesarCipher n = map (num2let . (`mod` 26) . (n+) . let2num)

Приятно то, что здесь вы можете повторно использовать let2num и num2let для других функций.

Обычно функции верхнего уровня разделяются с помощью пустая строка и подпись. Это не необходимо , но обычно делает его более удобным для чтения.

...