简体   繁体   English

是什么导致了`Prelude.chr: bad argument`?

[英]What causes `Prelude.chr: bad argument`?

I have the following Haskell program I wrote, the purpose of which is to function like a Caesar cipher :我编写了以下 Haskell 程序,其目的是像凯撒密码一样运行

  1 import System.IO
  2 import System.Environment
  3 import System.Exit
  4 import Data.Char
  5 
  6 shiftRight :: Int -> Char -> Char
  7 shiftRight shift char = do
  8   if isAsciiLower char
  9   then if (toEnum (fromEnum char + shift) :: Char) > 'z'
 10     then shiftRight (shift - 26) char
 11     else toEnum (fromEnum char + shift) :: Char
 12   else if isAsciiUpper char
 13     then if (toEnum (fromEnum char + shift) :: Char) > 'Z'
 14       then shiftRight (shift - 26) char
 15       else toEnum (fromEnum char + shift) :: Char
 16     else char
 17     
 18 shiftLeft :: Int -> Char -> Char
 19 shiftLeft shift char = do
 20   if isAsciiLower char  
 21   then if (toEnum (fromEnum char - shift) :: Char) < 'a'
 22     then shiftLeft (shift + 26) char
 23     else toEnum (fromEnum char - shift) :: Char
 24   else if isAsciiUpper char
 25     then if (toEnum (fromEnum char - shift) :: Char) < 'A'
 26       then shiftLeft (shift + 26) char
 27       else toEnum (fromEnum char - shift) :: Char
 28     else char
 29 
 30 main = do 
 31   args <- getArgs
 32   message <- getLine
 33   case args of
 34     [aString, aInt] -> 
 35       if aString == "-encode"
 36       -- `read` converts aInt from string to int
 37       -- `map` is used to apply `shiftRight` to each char in the string `message`
 38       then putStrLn $ show $ map (shiftRight $ read $ aInt) message
 39       else 
 40         if aString == "-decode"
 41         then putStrLn $ show $ map (shiftLeft $ read $ aInt) message
 42         else do
 43           putStrLn ("Second argument should be either '-decode' or '-encode'!")
 44           exitFailure
 45     _ -> do 
 46       progName <- getProgName
 47       putStrLn ("Usage: " ++ progName ++ " [-encode|-decode] [0-9]")
 48       exitFailure

My ghc version is as follows:我的ghc版本如下:

$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 8.6.5

I compile the Haskell on macos (Catalina):我在 macos (Catalina) 上编译 Haskell:

$ ghc Prog1d.hs -o Prog1d
Loaded package environment from $HOME/.ghc/x86_64-darwin-8.6.5/environments/default
[1 of 1] Compiling Main             ( Prog1d.hs, Prog1d.o )
Linking Prog1d ...

Then I run my code:然后我运行我的代码:

$ echo "ABCXYZabcxyz" | ./Prog1d -encode 1
"BCDYZAbcdyza"
$ echo "ABCXYZabcxyz" | ./Prog1d -encode 2
"CDEZABcdezab"
$ echo "ABCXYZabcxyz" | ./Prog1d -encode 4
"EFGBCDefgbcd"
$ echo "ABCXYZabcxyz" | ./Prog1d -encode 100
"WXYTUVwxytuv"
$ echo "ABCXYZabcxyz" | ./Prog1d -decode 1
Prog1d: Prelude.chr: bad argument: (-14)
$ echo "ABCXYZabcxyz" | ./Prog1d -decode 2
Prog1d: Prelude.chr: bad argument: (-15)
$ echo "ABCXYZabcxyz" | ./Prog1d -decode 4
Prog1d: Prelude.chr: bad argument: (-17)
$ echo "ABCXYZabcxyz" | ./Prog1d -decode 100
Prog1d: Prelude.chr: bad argument: (-35)

Why do I get Prelude.chr: bad argument ?为什么我得到Prelude.chr: bad argument What causes this, and what can I do to fix the problem?这是什么原因造成的,我该怎么做才能解决这个问题?

I have read about others who have had this error, but in their case, deleting the *.hi files solved the problem.我读过其他人遇到过这个错误,但在他们的情况下,删除*.hi文件解决了这个问题。 I have deleted Prog1d.hi (as well as Prog1d.o and Prog1d ), but to no effect.我已经删除了Prog1d.hi (以及Prog1d.oProg1d ),但没有任何效果。 I feel this may be caused by something in my code, maybe with line 41:我觉得这可能是由我的代码中的某些内容引起的,可能是第 41 行:

then putStrLn $ show $ map (shiftLeft $ read $ aInt) message

But this line is just like line 38, which works just fine for the -encode use-case.但是这一行就像第 38 行一样,对于-encode用例来说效果很好。 I must be missing something obvious.我一定遗漏了一些明显的东西。

I am new to Haskell, so please help me along.我是 Haskell 的新手,所以请帮助我。 I am mostly used to writing code in imperative languages like C++, python, Java, etc. and I am not yet familiar with the ideas and syntax of Haskell and other functional languages.我主要习惯于使用命令式语言(如 C++、python、Java 等)编写代码,我还不熟悉 Haskell 和其他函数式语言的思想和语法。

Thanks for reading this!感谢您阅读本文!

I found the problem.我发现了问题。

In my code, I convert a character into its corresponding ASCII code.在我的代码中,我将一个字符转换为其相应的 ASCII 代码。

For example, fromEnum 'A' will give you 65 .例如, fromEnum 'A'会给你65 The problem is that, if you shift the value off the table of ASCII values, eg -1 , you cause a bad argument error for the toEnum I use to compare for checking if the shift went too far.问题是,如果您将值从 ASCII 值表中移出,例如-1 ,则会导致我用来比较以检查移位是否过大的toEnum的错误参数错误。

For example:例如:

echo "A" | ./Prog1d -decode 66

This will take me to line 25 in my program:这将带我到程序中的第 25 行:

25     then if (toEnum (fromEnum char - shift) :: Char) < 'A'

fromEnum char will be fromEnum 'A' which will give me 65 . fromEnum char将是fromEnum 'A'这会给我65

Then I subtract from 65 the shift value: 66 .然后我从65减去shift值: 66 So, 65 - 66 = -1 .所以, 65 - 66 = -1

Then toEnum -1 causes Prelude.chr: bad argument: (-1) .然后toEnum -1导致Prelude.chr: bad argument: (-1)

That's because there is no ASCII character for -1 and that value is out of bounds.那是因为-1没有 ASCII 字符并且该值超出范围。

This means I simply have to compare int values instead of chars to check whether my shift is out of bounds or not.这意味着我只需要比较 int 值而不是字符来检查我的移位是否超出范围。

Here is the corrected code:这是更正后的代码:

import System.IO
import System.Environment
import System.Exit
import Data.Char
 
shiftRight :: Int -> Char -> Char
shiftRight shift char = do
  if isAsciiLower char 
  then if (fromEnum char + shift) > 122 -- 122 is 'z' in ASCII
    then shiftRight (shift - 26) char
    else toEnum (fromEnum char + shift) :: Char
  else if isAsciiUpper char
    then if (fromEnum char + shift) > 90 -- 90 is `Z` in ASCII
      then shiftRight (shift - 26) char
      else toEnum (fromEnum char + shift) :: Char
    else char
 
shiftLeft :: Int -> Char -> Char
shiftLeft shift char = do
  if isAsciiLower char 
  then if (fromEnum char - shift) < 97 -- 97 is 'a' in ASCII
    then shiftLeft (shift - 26) char
    else toEnum (fromEnum char - shift) :: Char
  else if isAsciiUpper char
    then if (fromEnum char - shift) < 65 -- 65 is 'A' in ASCII
      then shiftLeft (shift - 26) char
      else toEnum (fromEnum char - shift) :: Char
    else char
 
main = do
  args <- getArgs
  message <- getLine
  case args of
    [aString, aInt] -> 
      if aString == "-encode"
      -- `read` converts aInt from string to int
      -- `map` is used to apply `shiftRight` to each char in the string `message`
      then putStrLn $ map (shiftRight $ read $ aInt) message
      else 
        if aString == "-decode"
        then putStrLn $ map (shiftLeft $ read $ aInt) message
        else do
          putStrLn ("Second argument should be either '-decode' or '-encode'!")
          exitFailure
    _ -> do 
      progName <- getProgName
      putStrLn ("Usage: " ++ progName ++ " [-encode|-decode] [0-9]")
      exitFailure

And here is the proper, desired output:这是正确的,所需的输出:

$ echo "ABCXYZabcxyz" | ./Prog1d -encode 1
BCDYZAbcdyza
$ echo "ABCXYZabcxyz" | ./Prog1d -encode 2
CDEZABcdezab
$ echo "ABCXYZabcxyz" | ./Prog1d -encode 4
EFGBCDefgbcd
$ echo "ABCXYZabcxyz" | ./Prog1d -encode 100
WXYTUVwxytuv
$ echo "ABCXYZabcxyz" | ./Prog1d -decode 1
ZABWXYzabwxy
$ echo "ABCXYZabcxyz" | ./Prog1d -decode 2
YZAVWXyzavwx
$ echo "ABCXYZabcxyz" | ./Prog1d -decode 4
WXYTUVwxytuv
$ echo "ABCXYZabcxyz" | ./Prog1d -decode 100
EFGBCDefgbcd

EDIT (2020-09-01): John Purdy gave me some feedback, which prompted me to radically refactor my code.编辑(2020-09-01):John Purdy 给了我一些反馈,这促使我从根本上重构我的代码。 Here is the improved version:这是改进的版本:

import System.IO
import System.Environment
import System.Exit
import Data.Char

shift :: Int -> Char -> Char
shift amount char
  -- `ord` is `fromEnum` but only for `Char` types; converts a Char to an Int, 
  -- which gives us the ASCII number for that character
  -- see: https://hackage.haskell.org/package/base-4.14.0.0/docs/Data-Char.html#v:ord
  | isAsciiLower char && (shifted < ord 'a' || shifted > ord 'z') = shift cycled char
  -- `chr` is `toEnum` but only for `Char` types; it converts from an Int to Char
  -- see: https://hackage.haskell.org/package/base-4.14.0.0/docs/Data-Char.html#v:chr
  | isAsciiLower char = chr shifted
  | isAsciiUpper char && (shifted < ord 'A' || shifted > ord 'Z') = shift cycled char
  | isAsciiUpper char = chr shifted 
  | otherwise = char
  where shifted = ord char + amount -- e.g. 'A' (ASCII: 66) shifted 1 = 'B' (ASCII: 67), so shifted would be 67. 
        -- cycled: the shift amount, but cycled by 26 to start over the alphabet, 
        -- e.g. 'Z' (by ASCII: 90) shifted 1 is 91, so -26 to get 65, which is 'A'
        -- the amount `div` amount makes sure we add or subtract 26 as needed to cycle 
        -- e.g. 'A' (65) shifted -1 is 64, but if we subtracted 26 from 64, it wouldn't cycle us to Z, 
        -- it is in the wrong direction
        -- so instead we multiply by the sign of the amount: -1 - (26 * (-1 / |-1|) to get +25, 
        -- so 65 ('A') + 25 = 90 ('Z')
        cycled = amount - ((amount `div` abs (amount)) * 26) 

main = do
  args <- getArgs
  message <- getLine
  case args of
      -- `read` converts aInt from string to int
      -- `map` is used to apply `shiftRight` to each char in the string `message`
    ["-encode", aInt] -> putStrLn $ map (shift $ read $ aInt) message
    ["-decode", aInt] -> putStrLn $ map (shift $ negate $ read $ aInt) message
    _ -> do 
      progName <- getProgName
      putStrLn ("Usage: " ++ progName ++ " [-encode|-decode] [0-9]")
      exitFailure

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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