【问题标题】:Point-free style and using $无点样式并使用 $
【发布时间】:2023-04-09 04:59:01
【问题描述】:

如何结合使用$ 和无点样式?

一个明显的例子是下面的实用函数:

times :: Int -> [a] -> [a]
times n xs = concat $ replicate n xs  

只写concat $ replicate 会产生错误,同样你也不能写concat . replicate,因为concat 需要一个值而不是一个函数。

那么你会如何把上面的函数变成无点样式呢?

【问题讨论】:

标签: haskell pointfree


【解决方案1】:

你可以使用这个组合器:(冒号暗示后面跟着两个参数)

(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
(.:) = (.) . (.)

它可以让你摆脱n:

time = concat .: replicate

【讨论】:

  • @dave4420 好吧,恕我直言,.: 更容易记忆
  • 我个人更喜欢.*,这样下一个可以是.**.***等。无论哪种方式,我们都应该尝试让.:进入Haskell Prime的Prelude,或者在最少进入基础库。
  • fmap fmap fmap.:的泛化吗?
  • @nponeccop 是的,但是一个就像 (.),所以它是 fmap 。 fmap。
【解决方案2】:

您可以轻松地编写一个几乎没有点的版本

times n  =  concat . replicate n

使用显式 curry 和 uncurry 可以实现完全无点的版本:

times  =  curry $ concat . uncurry replicate

【讨论】:

  • 恕我直言,最后一个过于复杂,因为它涉及不必要的curryuncurry,请参阅 Josh 和我的回答。
  • @FUZxxl:我知道,我赞成你的回答,我们现在都在 +3。
  • 几乎无积分版本的另一个 +1。虽然我建议更广泛地采用.:,但我也建议在更复杂的情况下坚持使用“几乎”无点。
  • @Dan 好吧,完全无点通常被认为太没有意义了,至少在我看来。
  • 和我一起 :) 根据您的技能和品味调整无点数。通过研究组合逻辑(终极无点微积分)可以观察到无点风格的一些基本缺陷。还可以看看 Tony Hoare 在基于函数的编程方面的工作。
【解决方案3】:

上 freenode 并询问 lambdabot ;)

<jleedev> @pl \n xs -> concat $ replicate n xs
<lambdabot> (join .) . replicate

【讨论】:

  • 请注意 (foo . ) . bar 是 lambdabot 对 foo .: bar 的典型模式,因为 .: 显然没有在无点处理过程中考虑。
【解决方案4】:

在 Haskell 中,函数组合是关联的¹:

f . g . h == (f . g) . h == f . (g . h)

任何infix operator 都只是一个很好的函数:

2 + 3 == (+) 2 3
f 2 3 = 2 `f` 3

composition operator 也只是一个二进制函数,higher-order 一个,它接受 2 个函数并返回一个函数:

(.) :: (b -> c) -> (a -> b) -> (a -> c)

因此,任何组合运算符都可以这样重写:

f . g == (.) f g
f . g . h == (f . g) . h == ((.) f g) . h == (.) ((.) f g) h
f . g . h == f . (g . h) == f . ((.) g h) == (.) f ((.) g h)

由于默认情况下currying,Haskell 中的每个函数都可以是partially applied。中缀运算符可以以非常简洁的方式部分应用,使用sections

(-) == (\x y -> x - y)
(2-) == (-) 2 == (\y -> 2 - y)
(-2) == flip (-) 2 == (\x -> (-) x 2) == (\x -> x - 2)
(2-) 3 == -1
(-2) 3 == 1

由于组合运算符只是一个普通的二元函数,你也可以分段使用:

f . g == (.) f g == (f.) g == (.g) f

另一个有趣的二元运算符是$,它只是函数应用程序:

f x == f $ x
f x y z == (((f x) y) z) == f x y z
f(g(h x)) == f $ g $ h $ x == f . g . h $ x == (f . g . h) x

有了这些知识,我如何将concat $ replicate n xs 转换为无点样式?

times n xs = concat $ replicate n xs
times n xs = concat $ (replicate n) xs
times n xs = concat $ replicate n $ xs
times n xs = concat . replicate n $ xs
times n    = concat . replicate n
times n    = (.) concat (replicate n)
times n    = (concat.) (replicate n) -- concat is 1st arg to (.)
times n    = (concat.) $ replicate n
times n    = (concat.) . replicate $ n
times      = (concat.) . replicate

¹Haskell 基于category theory。范畴论中的范畴由 3 个事物组成:一些对象、一些态射和态射的组合概念。每个态射单向连接一个源对象和一个目标对象。范畴论要求态射的组合是关联的。 Haskell 中使用的一个类别称为 Hask,其对象是类型,而其态射是函数。函数f :: Int -&gt; String 是将对象Int 连接到对象String 的态射。因此范畴论要求 Haskell 的函数组合具有关联性。

【讨论】:

    【解决方案5】:

    通过扩展 FUZxxl 的答案,我们得到了

    (.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
    (.:) = (.).(.)
    
    (.::) :: (d -> e) -> (a -> b -> c -> d) -> a -> b -> c -> e
    (.::) = (.).(.:)
    
    (.:::) :: (e -> f) -> (a -> b -> c -> d -> e) -> a -> b -> c -> d -> f
    (.:::) = (.).(.::)
    
    ...
    

    非常好。

    奖金

    (.:::) :: (e -> f) -> (a -> b -> c -> d -> e) -> a -> b -> c -> d -> f
    (.:::) = (.:).(.:)
    

    嗯...所以也许我们应该说

    (.1) = .
    
    (.2) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
    (.2) = (.1).(.1)
    
    (.3) :: (d -> e) -> (a -> b -> c -> d) -> a -> b -> c -> e
    (.3) = (.1).(.2)
    -- alternatively, (.3) = (.2).(.1)
    
    (.4) :: (e -> f) -> (a -> b -> c -> d -> e) -> a -> b -> c -> d -> f
    (.4) = (.1).(.3)
    -- alternative 1 -- (.4) = (.2).(.2)
    -- alternative 2 -- (.4) = (.3).(.1)
    

    更好。

    我们也可以把它扩展到

    fmap2 :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b)
    fmap2 f = fmap (fmap f)
    
    fmap4 :: (Functor f, Functor g, Functor h, functro i) 
       => (a -> b) -> f (g (h (i a))) -> f (g (h (i b)))
    fmap4 f = fmap2 (fmap2 f)
    

    遵循相同的模式。

    将应用fmap(.) 的时间参数化会更好。但是,那些fmap(.)s 实际上在类型上有所不同。所以这样做的唯一方法是使用编译时间计算,例如TemplateHaskell

    对于日常使用,我只是建议

    Prelude> ((.).(.)) concat replicate 5 [1,2]
    [1,2,1,2,1,2,1,2,1,2]
    Prelude> ((.).(.).(.)) (*10) foldr (+) 3 [2,1]
    60
    

    【讨论】:

      猜你喜欢
      • 2015-02-17
      • 1970-01-01
      • 1970-01-01
      • 2016-11-25
      • 1970-01-01
      • 2018-06-23
      • 1970-01-01
      • 2018-06-16
      • 1970-01-01
      相关资源
      最近更新 更多