【问题标题】:Uses for Haskell id functionHaskell id 函数的用途
【发布时间】:2011-03-09 08:35:32
【问题描述】:

id function 在 Haskell 中有哪些用途?

【问题讨论】:

  • 据我所知,最有可能写出糟糕的代码:)

标签: function haskell functional-programming


【解决方案1】:

作为higher order functions(将函数作为参数的函数)的参数很有用,您希望某些特定值保持不变。

示例 1:如果某个值在 Just 中,则不考虑该值,否则返回默认值 7。

Prelude Data.Maybe> :t maybe
maybe :: b -> (a -> b) -> Maybe a -> b

Prelude Data.Maybe> maybe 7 id (Just 2)
2

示例 2:通过折叠构建函数:

Prelude Data.Maybe> :t foldr (.) id [(+2), (*7)]
:: (Num a) => a -> a

Prelude Data.Maybe> let f = foldr (.) id [(+2), (*7)]

Prelude Data.Maybe> f 7
51

我们使用id 作为基本情况,通过将函数列表与(.) 折叠在一起构建了一个新函数f

示例 3:作为幺半群的函数的基本情况(简化)。

instance Monoid (a -> a) where
        mempty        = id
        f `mappend` g = (f . g)

与我们的 fold 示例类似,函数可以被视为可连接的值,id 用于空大小写,(.) 作为附加值。

示例 4:一个普通的哈希函数。

Data.HashTable> h <- new (==) id :: IO (HashTable Data.Int.Int32 Int)

Data.HashTable> insert h 7 2

Data.HashTable> Data.HashTable.lookup h 7
Just 2

哈希表需要哈希函数。但是如果你的密钥已经被散列了怎么办?然后传递 id 函数,填充为你的哈希方法,性能开销为零。

【讨论】:

  • 使用foldl' (.) id 比使用foldr (.) id 会更好吗?
  • 在这种情况下不是。严格性不会对函数组合产生任何影响。
  • @Don Stewart,我希望编译器对某些函数(如 (+) 和其他需要绑定两个参数的外部函数)做一些可怕的错误,例如更改应用程序树以折叠某些节点和他们使用的空闲内存。
  • 与此相关的另一个示例是使用 id 作为继续传递样式 (CPS) 中的基本函数。
  • 我的最爱之一:numberOfTrues :: [Bool] -&gt; Int; numberOfTrues = length . filter id
【解决方案2】:

在函数式语言中,函数是一等值 您可以作为参数传递。 因此,id 最常见的用法之一出现在 你传递一个函数作为 参数给另一个函数来告诉它要做什么。 做什么的选择之一可能是 “别管它”——在这种情况下,你通过id 作为参数。

【讨论】:

  • just leave it alone 是让我点击的部分,谢谢!
  • 其中一种选择可能是“别管它”——在这种情况下,你将 id 作为参数传递 你的意思是多态行为函数 - 意思是 HOF 已经接受了一些函数作为带有签名 T->T 的参数,即输入和 o/p 类型相同,并且在某些条件下,您可以传递 Id 函数,该函数也具有相同类型的 i/p 和 o/p 加上相同甚至值?
【解决方案3】:

换一种答案:

在通过组合链接多个函数时,我经常会这样做:

foo = id
  . bar
  . baz
  . etc

结束

foo = bar
  . baz
  . etc

它使编辑更容易。人们可以用其他“零”元素做类似的事情,例如

foo = return
  >>= bar
  >>= baz

foos = []
  ++ bars
  ++ bazs

【讨论】:

  • 聪明,但你真的在真实代码中做到这一点吗?这几乎让我渴望 Java。
  • 如果组合不能整齐地放在一行上,我会用真实的代码来做。这通常发生在函数名很长和/或有很多有意义的参数时。
【解决方案4】:

如果您操作数字,尤其是加法和乘法,您会注意到 0 和 1 的用处。同样,如果您操作列表,空列表会非常方便。同样,如果您操作函数(在函数式编程中很常见),您会注意到 id 的相同用途。

【讨论】:

    【解决方案5】:

    假设您正在寻找某种谜题的解决方案,在该谜题中,您在每个回合都采取行动。您从候选职位pos 开始。在每个阶段,您都可以对pos 进行可能的转换列表(例如,在拼图中滑动一块)。在函数式语言中,将转换表示为函数是很自然的,因此现在您可以使用函数列表制作移动列表。如果“什么都不做”在这个谜题中是合法的,那么你可以用id 来表示它。如果您不这样做,那么您需要将“什么都不做”作为与“做某事”不同的特殊情况来处理。通过使用id,您可以在一个列表中统一处理所有案例。

    这可能是几乎所有id 的用法都存在的原因。将“无为”与“做某事”统一处理。

    【讨论】:

    • 你的意思是函数的多态行为 - 意思是 HOF 已经接受了一些函数作为带有签名 T->T 的参数,即输入和 o/p 类型相同,在某些条件下你可以传递 Id 函数也有相同类型的 i/p 和 o/p 加上相同的值?
    【解决方案6】:

    每当您需要在某个地方拥有一个函数,但想做的不仅仅是保持它的位置(以“未定义”为例)。

    正如上面提到的(即将成为)斯图尔特博士所说,当您需要将函数作为参数传递给另一个函数时,它也很有用:

    join = (>>= id)
    

    或作为函数的结果:

    let f = id in f 10
    

    (大概,你稍后会编辑上面的函数来做一些更“有趣”的事情......;)

    正如其他人所提到的,id 是一个很好的占位符,当您需要某个功能时。

    【讨论】:

      【解决方案7】:

      我还可以帮助您提高高尔夫成绩。而不是使用

      ($)

      您可以使用 id 保存单个字符。

      例如

      zipWith id [(+1), succ] [2,3,4]

      一个有趣的,不仅仅是有用的结果。

      【讨论】:

      • 在 qwerty 键盘上键入 ($) 也很痛苦,所以我非常喜欢尽可能使用 id。
      • 如果空格很重要,那也无济于事。 zipWith($)[][]zipWith id[][].
      【解决方案8】:

      因为我们找到了id 的不错应用。在这里,有一个回文:)

      import Control.Applicative
      
      pal :: [a] -> [a]
      pal = (++) <$> id <*> reverse
      

      【讨论】:

      • @MiguelRodrigues 在这里:) &lt;*&gt; reverse 建议 instance Applicative ((-&gt;) r) 其中 (f &lt;*&gt; g) x = f x (g x)($) = (.) 所以 pal x = ((++).id) x (reverse x) = x ++ reverse x。 (这表明pal = (++) &lt;*&gt; reverse 也有效)。
      【解决方案9】:

      假设您是一台计算机,即您可以执行一系列步骤。那么如果我想让你保持现在的状态,但我总是要给你一个指令(我不能只是沉默,让时间过去),我给你什么指令? Id 是为此创建的函数,用于返回不变的参数(在以前的计算机的情况下,参数将是它的状态)并为其命名。只有当你有高阶函数时,这种必要性才会出现,当你使用函数而不考虑它们内部的内容时,这迫使你象征性地表示甚至“什么都不做”的实现。类似地,0 被视为某种事物的数量,是没有数量的象征。实际上在代数中 0 和 id 分别被认为是操作 + 和 ∘(函数组合)的中性元素,或者更正式地说:

      对于所有类型编号的 x:

      • 0 + x = x
      • x + 0 = x

      对于所有 f 类型的函数:

      • id ∘ f = f
      • f ∘ id = f

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-10-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-28
        • 2011-03-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多