简体   繁体   中英

Correct way to format Haskell functions considering scope?

I'm new to Haskell. I've put together a basic Caesar Cipher, it works, but it's very messy and difficult to read.

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

What is the "correct" way in Haskell to format functions that consist of multiple variables and expressions, and should I be considering the scope of the variables? And other than efficiency based suggestions have I made any other "major" mistakes?

This is my attempt:

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]

I would first of all rewrite some functions. For example. zip alphabet [0 .. length alphabet] can be replaced with zip alphabet [0..] , since the zip will stop from the moment one of the lists is exhausted. Making use of (!!) and head is often not good practice, since these functions are non-total: if the index is too large, or the list is empty, (!!) and head will error respectively.

We can define helper functions, for example for num2let :

import Data.Char(chr, ord)

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

here num2let will map 0 to 'a' , 1 to 'b' , etc.

let2num can be done in a similar manner:

import Data.Char(ord)

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

So now we can define caesarCipher as:

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

So that would look in full as:

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)

The nice thing is that you can here reuse the let2num and num2let for other functions.

Normally top-level functions are separated with a blank line, and are given a signature. This is not necessary , but makes it usually more convenient to read.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM