【问题标题】:Palindrome checker that ignores non-alphanumeric characters and case Haskell忽略非字母数字字符和大小写 Haskell 的回文检查器
【发布时间】:2018-12-18 00:38:16
【问题描述】:

需要制作一个程序来检查给定字符串是否为回文,无论大小写是否不同,它都应该工作,并且应该忽略非字母数字字符,仅使用 Data.char 中的 ord 和 chr 函数和常规函数,没有其他的。我能够创建常规回文检查器:

reverseStr::String->String
reverStr s | s == [] = []
reverseStr (h:t) = reverseStr t ++ [h]

isPalindrome :: String -> Bool
isPalindrome s = s == reverseStr s

我已经开始研究一个函数来规范化大小写:

normalizeCase::String->String
normalizeCase h | h == [] = []
normalizeCase (h) = if ord h > 64 && ord h < 123
    then map (chr $ (ord h + 32)) [h]
    else h

但我收到以下错误:

• Couldn't match expected type ‘Char -> Char’
              with actual type ‘Char’
• In the first argument of ‘map’, namely ‘(chr $ (ord h + 32))’
  In the expression: map (chr $ (ord h + 32)) [h]
  In the expression:
    if ord h > 64 && ord h < 123 then
        map (chr $ (ord h + 32)) [h]
    else
        h

  |
6 |   then map (chr $ (ord h + 32)) [h]   |             ^^^^^^^^^^^^^^^^^^


    • Couldn't match type ‘Char’ with ‘[Char]’
  Expected type: String
    Actual type: Char
• In the expression: h
  In the expression:
    if ord h > 64 && ord h < 123 then
        map (chr $ (ord h + 32)) [h]
    else
        h
  In an equation for ‘normalizeCase’:
      normalizeCase [h]
        = if ord h > 64 && ord h < 123 then
              map (chr $ (ord h + 32)) [h]
          else
              h
  |
7 |   else h   |        ^

我对 Haskell 还是很陌生,不知道如何正确实现 ord 或 chr 以便它与这个检查器一起工作,所以任何帮助都将不胜感激!

【问题讨论】:

  • 一种方法是规范化大小写,例如,在检查回文之前将所有字符变为大写。作为第一步,你能弄清楚如何使用ord 判断一个字符是否为小写吗?试一试ghci 中的函数,看看你是否能注意到一个规律。
  • normalizeCase::String->String normalizeCase h | h == [] = [] normalizeCase [h] = if ord h > 64 && ord h
  • Either 使用map 在列表上“循环”,或手动递归模式匹配解构。 (最好是map。)你似乎试图同时做这两个......

标签: haskell functional-programming palindrome


【解决方案1】:

在这段代码中:

normalizeCase::String->String
normalizeCase h | h == [] = []
normalizeCase (h) = if ord h > 64 && ord h < 123
    then map (chr $ (ord h + 32)) [h]
    else h

您的第二个模式(h)(仅相当于h)匹配任何列表(只要它还没有被第一个模式匹配,h | h == []);所以h 是一个列表,ord h 没有意义,因为ord 需要Char,而不是[Char]

假设h 是一个字符,那么chr $ ord h + 32 也是一个字符,但map 需要一个函数 作为它的第一个参数;这是 Char -&gt; Char 预期的错误来源,但您给出了 Char。对于map 的第二个参数,您传递[h],它是单个元素h 的列表(在您的代码中也是一个列表,因此您在需要[Char] 时提供[[Char]]) .

同样假设h 是一个字符,你的条件ord h &gt; 64 &amp;&amp; ord h &lt; 123 匹配大写 A小写 z 之间的任何字符,包括你的几个字符不想要 ([]^_`)。 h 是一个 list 字符的事实是错误的来源,Char 是预期的,但你给了 [Char]

您似乎也将递归样式与map 混合在一起——在这种情况下,您应该要么使用map 按情况定义函数。

以下是修复这些错误后您的代码的外观。一、使用递归:

normalizeCase :: String -> String

-- Given an empty list, return an empty list.
-- This happens if the original input was empty,
-- or when we reach the end of the recursion.
normalizeCase [] = []

-- Given a non-empty list,
-- test the first character ‘c’.
normalizeCase (c : cs) = if c >= 'A' && c <= 'Z'

  -- If it’s uppercase, lowercase it and
  -- prepend it to the result of normalizing
  -- the remainder of the string ‘cs’.
  then chr (ord c + 32) : normalizeCase cs

  -- Otherwise, don’t change it, but still
  -- prepend it to the result of normalizing
  -- the remainder of the string ‘cs’.
  else c : normalizeCase cs

或者,使用map

normalizeCase :: String -> String

-- Given any string:
normalizeCase s

  -- For each character:
  = map

    -- If it’s uppercase, lowercase it.
    (\ c -> if c >= 'A' && c <= 'Z'
      then chr (ord c + 32)
      else c)

    -- Over the whole string.
    s

Char 可以直接比较 (c &gt;= 'A'),这样更易​​读,但如果您也希望使用ord 进行比较,那就是ord c &gt;= 65

我知道你不应该为这个任务使用任何其他标准函数,但为了将来参考,这也可以使用来自Data.ChartoLower 非常简单地实现:

import Data.Char (toLower)

normalizeCase :: String -> String
normalizeCase s = map toLower s

-- Alternatively, a point-free/eta-reduced version:
normalizeCase = map toLower

对于删除非字母数字字符的额外任务,您可以使用filter,一个带有保护条件的列表推导,或者自己编写一个直接递归版本。

【讨论】:

  • 非常感谢,在检查后能够弄清楚其余部分,非常感谢您的帮助!
猜你喜欢
  • 2017-09-05
  • 1970-01-01
  • 2016-06-06
  • 2015-07-18
  • 2016-03-02
  • 1970-01-01
  • 2013-12-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多