【问题标题】:Haskell + "Remove a parameter"Haskell +“删除参数”
【发布时间】:2014-11-07 10:27:45
【问题描述】:

我有一个理论问题,我和一个朋友一直在尝试了解我们如何做到这一点。我们有一个名为palindrome 的函数,它告诉我们字符串是否是回文。以下是我们实现它的方式(可能有更好的方法来实现它,但这就是我们更喜欢它的方式。)

palindrome :: String -> Bool
palindrome = \x -> (== reverse x) x

我们想要做的是remove 我们在这里使用的 Lambda,只使用部分应用程序和高阶函数。我们有几个关于如何做到这一点的想法,但没有一个与我们制作的函数类型相匹配:

palindrome :: String -> Bool

最接近的方法之一是使用组合,但它返回给我们一个函数,它接受两个字符串而不是一个,因为一个用于反向,另一个用于 (==):

palindrome = (==) . reverse

也许,我们忘记了什么,或者我们没有看到什么。您将如何在不使用 Lambda 的情况下使用这两个功能?

【问题讨论】:

  • 问题不清楚。你想要一个无积分版本还是palindrome x = x == reverse x 想要?
  • 我想要一个没有参数的版本,只是使用函数来摆脱我们正在使用的 lambda。到目前为止,所有答案看起来都不错。对不起,如果我的问题不够清楚。
  • @DavidCorrea 以编程方式获得此类缩减的提示(尽管并不总是以可理解的形式)是使用pointfree 工具。只需向它传递一个像 palindrome x = x == reverse x 这样的表达式,它就会返回它可以导出的最无点形式。

标签: haskell lambda functional-programming


【解决方案1】:

您可以将<*> 运算符用于(->)Applicative 实例。

palindrome = (==) <*> reverse

&lt;*&gt; 运算符在某些情况下有点像函数应用程序。在这种情况下,该上下文是“以String 作为参数的函数”。所以你得到一个函数,它接受 String 并将它传递给两个同样接受 Strings 的函数,然后将前者的结果应用于后者。换句话说,f &lt;*&gt; g = \x -&gt; f x (g x)

【讨论】:

    【解决方案2】:

    你现在已经得到了三个答案,但可能还不是很了解。

    问题是你的右手边有两个 x,看起来没有一个好的方法可以删除一个。您需要一个具有以下签名之一的函数来“复制”该 x:

    x -> (x, x)
    x -> (x -> a, x -> b) -> (a, b)
    ((x, x) -> y) x -> y
    (x -> x -> y) -> x -> y
    (x -> y) -> (x -> y -> z) -> (x -> z)
    

    这些实际上很难找到;我原以为其中的第一个将以dup x = (x, x) 的名义提供,但无处可寻。据我所知,这些在前奏曲中都找不到。

    事实证明,有一个名为“Reader”的 Monad 将一个值(在本例中为您的 x)通过多个函数线程化。这个 monad 的 type(-&gt;) x,这意味着它是所有 y 的函数 x -&gt; y(x -&gt; x -&gt; y) -&gt; x -&gt; y 上面的倒数第二个函数实际上可以写成:

    Prelude> :m + Control.Monad
    Prelude Control.Monad> :t (id >>=)
    (id >>=) :: (a -> a -> b) -> a -> b
    

    因此你可以只写:

    id >>= (==) . reverse
    

    或者更简洁:

    palindrome = reverse >>= (==)
    

    原来大家的版本在幕后偷偷使用(&gt;&gt;=)。例如,每个Monad 都是一个Applicative,而Control.Applicative 库导出一个符号(&lt;*&gt;) :: Applicative f =&gt; f (x -&gt; y) -&gt; f x -&gt; f y。在f(-&gt;) a 的情况下,我们有:(&lt;*&gt;) :: (a -&gt; x -&gt; y) -&gt; (a -&gt; x) -&gt; y,我们可以申请(==)reverse 来获得:

    palindrome = (==) <*> reverse
    

    或者Control.Monad 具有相同的instance Monad ((-&gt;) x) 并为其导出&gt;&gt;=(如上,(x -&gt; y) -&gt; (y -&gt; x -&gt; z) -&gt; x -&gt; zjoin :: m (m x) -&gt; m x,在这种情况下为(x -&gt; x -&gt; y) -&gt; x -&gt; y

    有很多选项,但实例声明是在这些库中进行的。如果你想在没有这些库的情况下做到这一点,你必须写出乏味的东西:

    instance Monad ((->) x) where
        return = const
        yx >>= zxy = \x -> zxy (yx x) x
    

    在您应用这些功能之前。在这一点上,它几乎更容易编写:

    Prelude> let dup x = (x, x)
    Prelude> let palindrome = uncurry ((==) . reverse) . dup
    Prelude> :t palindrome
    palindrome :: Eq a => [a] -> Bool
    

    【讨论】:

    • +1 表示reverse &gt;&gt;= (==)。但是,请注意f &gt;&gt;= g 不等于g &lt;*&gt; f 而是flip g &lt;*&gt; f。您可以使用g = (,) 说服自己。
    • 即使所有的答案都很好,我会选择这个作为对每个解决方案的所有解释的正确答案。它真的很有用。箭头方法也很有趣。
    【解决方案3】:

    你可以从Control.Monad使用join

    palindrome = join ((==) . reverse)
    

    join 是 monad 的基本功能。但是,对于这种特殊情况,即函数(或者更准确地说,Monad(-&gt;) 实例),join f x 的计算结果仅为 f x x

    【讨论】:

      【解决方案4】:

      更多的选择(虽然比以前的答案更糟糕;-))

      import Control.Arrow
      palindrome1 = (reverse &&& id) >>> uncurry (==)
      palindrome2 = ((==) &&& reverse) >>> uncurry ($)
      

      【讨论】:

      • 我在网上看到了 Arrows,我必须说它看起来很有趣。我是函数式编程的新手,到目前为止我很喜欢它。
      【解决方案5】:

      使用Control.Applicative

      palindrome = (==) <*> reverse
      

      【讨论】:

      • 打错字,已修复
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多