【问题标题】:Applying isAlpha to a recursive function将 isAlpha 应用于递归函数
【发布时间】:2020-11-30 16:10:00
【问题描述】:

我创建了函数filter,它在列表中找到最小的项目并过滤掉任何包含非字母字符的字符串。例如

filter ["a","bb","ccc","dddd","e","f"] = "a" 

但是如果我使用这个例子,isAlpha 似乎不起作用,我得到filter ["**", "a", "?"] = "**" 而不是= "a"

filter :: [String] -> String
filter [] = error "String is Empty"
filter [x] = x  
filter (x:y:xs) = if x < y && all isAlpha x then g(x:xs) else g(y:xs)

【问题讨论】:

  • 不要将你的函数命名为filter,因为它不是过滤器(尽管它可以实现使用 filter :: (a -&gt; Bool) -&gt; [a] -&gt; [a]定义在base 库)。
  • @CamelBak:请先指定函数。你如何在这里比较字符串?为什么"a" 小于"?"
  • 对不起。作为 ”?”不是按字母顺序排列的。
  • 使用&lt;比较字符串并不真正涉及字符串长度。字符串按字典顺序 排序,就像字典中的单词一样。在 Unicode 和 ASCII 序列中,字符 '*' 出现在 'a' 之前。因此,("*" &lt; "a")("*xyz" &lt; "a") 两个表达式都计算为 True。更多详情SO-q3651144
  • 为什么["**", "?"]会返回一个空字符串,而[]会报错?

标签: haskell


【解决方案1】:

恐怕我们需要为您的函数选择另一个名称,比如“smallest”,因为我们将需要库版本 filter: filter :: (a -&gt; Bool) -&gt; [a] -&gt; [a]

常规的minimum 函数,当应用于字符串时,返回一个按字典顺序排列的最小值,这绝对不是我们想要的。

但是,许多库函数,例如groupsortminimum,都有一个名称以“By”结尾的兄弟。当更简单的函数无法完成预期任务时,最终可以使用兄弟函数。兄弟姐妹接受一个额外的函数参数,允许调用者以某种方式自定义手头的任务。

例如,你有一个minimumBy 函数:

 λ> 
 λ> :type minimumBy
 minimumBy :: Foldable t => (a -> a -> Ordering) -> t a -> a
 λ> 

第一个天真的想法是尝试minimumBy length,但这甚至不会进行类型检查:函数length返回一个Int,而不是一个Ordering值,它可以是GTLT或@ 987654340@.

所以我们需要在minimumBylength 之间建立一些管道函数。管道函数的类型签名必须类似于Ord c =&gt; (a -&gt; c) -&gt; (a -&gt; a -&gt; Ordering),其中a 是String 类型,cInt 类型。

在从头开始编写这样的函数之前,我们可以检查它是否已包含在库中。我们通过将所需的类型签名提交到 Hoogle 来检查这一点。

Hoogle 立即points us 运行comparing

该功能完成了这项工作:

 λ>
 λ> import  Data.Ord
 λ>
 λ> :type comparing
 comparing :: Ord a => (b -> a) -> b -> b -> Ordering
 λ> 
 λ> :type  minimumBy (comparing length)
 minimumBy (comparing length)
  :: (Foldable t1, Foldable t2) => t1 (t2 a) -> t2 a
 λ> 
 λ> 
 λ> minimum ["aaa","bb","cccx","eer"]
 "aaa"
 λ> 
 λ> minimumBy (comparing length) ["aaa","bb","cccx","eer"]
 "bb"
 λ> 

初步任务包括消除那些包含非字母字符的字符串。这可以按照 Kevin P. Barry 的出色回答中的描述完成,并涉及 filter 库函数

总结:

import  Data.List  (minimumBy)
import  Data.Ord   (comparing)
import  Data.Char  (isAlpha)

smallest :: [String] -> String
smallest sts = let  alphas = filter  (all isAlpha)  sts
               in   if (null alphas)
                        then  error "No alphabetic strings in list"
                        else  minimumBy (comparing length) alphas

ghci下测试:

 λ> 
 λ> smallest ["aaa","bb","cccx","dddd-","eer","+"] 
"bb"
 λ> 

【讨论】:

    【解决方案2】:

    您想按长度而不是字符串本身进行比较,因此您正在寻找if length x &lt;= length y 而不是if x &lt; y&lt;= 确保使用平局中的第一次出现而不是最后一次出现。

    【讨论】:

    • 谢谢,但是如何检查字符串是否包含所有字母?
    【解决方案3】:

    使用Prelude中的filter,你可以只使用head . sort . filter (all isAlpha)吗?或者,如果您想保留当前的错误消息,head . (++["String is Empty"]) . sort . filter (all isAlpha)

    这里的关键是利用 Haskell 的 函数式 特性来发挥你的优势,而不是手写递归的东西。如果您知道单一用途函数的行为方式(例如,allfiltersort),那么您可以通过组合这些已知行为来构建您的函数。

    由于大多数 Haskell 函数都是惰性的,因此只会执行计算最终结果所需的计算。因此,如果只需要对部分元素进行排序以找到最小的,那么在运行时将跳过其他操作。

    您也可以增量以这种方式构建您的函数。比如从filter (all isAlpha)开始,然后做sort . filter (all isAlpha)等,看看原型的时候中间数据是什么。


    旁注:通常最好使用EitherMaybe 之类的东西来处理错误,以免它们被意外忽略。所以,head . (++[Nothing]) . map Just . sort . filter (all isAlpha) 会给你Nothing 如果没有字符串是 alpha,Just "a" (等等)表示成功。

    【讨论】:

    • 可以那样使用sort,利用base中实现sort的具体特性。但是使用minimum 会更清晰,而且可能更快。
    • @dfeuer 有时,一个简洁的解决方案可以证明一个观点,而不是一个高效但更混乱的解决方案。演示的要点是,您通常可以通过组合其他函数来解决问题;该解决方案不适用于生产。 minimum [] = ⊥ 和 minimum (xs ++ [Nothing]) = Nothing,这两者都使得指定过滤后列表为空时的行为变得复杂。使用minimum 可能需要某种分支,比如模式匹配、if/then/else| 等,这会掩盖重点。
    猜你喜欢
    • 1970-01-01
    • 2020-11-21
    • 2023-03-09
    • 2019-12-05
    • 2020-10-21
    • 1970-01-01
    • 2021-02-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多