首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >推导这个无点代码的步骤是什么?

推导这个无点代码的步骤是什么?
EN

Stack Overflow用户
提问于 2014-11-19 23:25:03
回答 3查看 274关注 0票数 7

我查看了一些代码,并发现了以下gem,我敢打赌它是pointfree输出的复制粘贴:

(我认为下面这个问题比通常的foo/bar更合适:P)

代码语言:javascript
复制
import Control.Monad (liftM2)

data Battleship = Battleship { x :: Int
                             , y :: Int
                             } deriving Show

placeBattleship :: Int -> Int -> Battleship
placeBattleship x' y' = Battleship { x = x', y = y' }

coordinates :: Battleship -> (Int, Int)
coordinates = liftM2 (,) x y

是否有人能解释一下需要简化的步骤:

(i) coordinates b = (x b, y b)

至:

(ii) coordinates = liftM2 (,) x y

特别是,我对liftM2的使用感到有点困惑,因为我甚至没有意识到后台隐藏着一个单播。

我知道(i)也可以表示为:coordinates s = (,) (x s) (y s),但我不知道在哪里/如何进行。

以下是我怀疑它来自pointfree的原因(输出来自GHCI:pl别名为pointfree):

代码语言:javascript
复制
λ: :pl coordinates s = (x s, y s)
coordinates = liftM2 (,) x y
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2014-11-19 23:42:54

这利用了(->) r(->) r实例,也称为"reader“。这是从特定类型到a的单一函数。(看一看here,了解它存在的原因。)

要了解它如何用于各种函数,请将m替换为m a中的(r ->。例如,如果我们只做liftM,就会得到:

代码语言:javascript
复制
liftM :: (a -> b) -> (m a -> m b)
liftM :: (a -> b) -> ((r -> a) -> (r -> b))
      :: (a -> b) -> (r -> a) -> (r -> b) -- simplify parentheses

...which只是函数组合。干净利落。

我们可以为liftM2做同样的事情

代码语言:javascript
复制
liftM2 :: (a -> b -> c) -> m a -> m b -> m c
liftM2 :: (a -> b -> c) -> (r -> a) -> (r -> b) -> (r -> c)

所以我们看到的是一种用两个参数函数组成两个单参数函数的方法。这是一种将正规函数组合推广到多个参数的方法。我们的想法是创建一个函数,通过两个单参数函数传递单个r,将两个参数传递到两个参数函数中。因此,如果我们有f :: (r -> a)g :: (r -> b)h :: (a -> b -> c),我们生产:

代码语言:javascript
复制
\ r -> h (f r) (h r)

现在,这如何应用于您的代码呢?(,)是两个参数函数,而xyBattleship -> Int类型的一个参数函数(因为字段访问器就是这样工作的)。考虑到这一点:

代码语言:javascript
复制
liftM2 (,) x y = \ r -> (,) (x r) (y r)
               = \ r -> (x r, y r)

一旦您内化了像这样的多功能组合的想法,像这样的无点代码就变得更加可读性了--不需要使用无点工具!在这种情况下,我认为无点版本仍然更好,但无点版本本身并不可怕。

票数 10
EN

Stack Overflow用户

发布于 2014-11-19 23:36:21

monad liftM2在这里工作,这是函数monad (->) a。正如您之前可能看到的,这相当于Reader monad。

回想一下liftM2的定义

代码语言:javascript
复制
liftM2 :: Monad m => (a -> b -> r) -> m a -> m b -> m r
liftM2 f ma mb = do
    a <- ma
    b <- mb
    return $ f a b

所以现在如果我们用(,)代替f,用x代替ma,用y代替mb,我们就可以得到

代码语言:javascript
复制
liftM2 (,) x y = do
    a <- x
    b <- y
    return $ (,) a b

因为x, y :: Battleship -> Int相当于((->) Battleship) Int,那么m ~ (->) Battleship。函数monad定义为

代码语言:javascript
复制
instance Monad ((->) a) where
    return x = const x
    m >>= f = \a -> f (m a) a

从本质上讲,函数monad所做的就是允许您从几个函数中提取输出,只要它们都具有相同的输入。一个更清楚的例子可能是

代码语言:javascript
复制
test = do
    a <- (^2)
    b <- (^3)
    c <- (^4)
    d <- show
    return (a, b, c, d)

> test 2
(4, 8, 16, "2")
票数 5
EN

Stack Overflow用户

发布于 2014-11-20 08:48:54

你可以很容易地重写

代码语言:javascript
复制
data Battleship = Battleship { x :: Int
                             , y :: Int
                             } deriving Show

placeBattleship :: Int -> Int -> Battleship
placeBattleship x y = Battleship x y

coordinates :: Battleship -> (Int, Int)
coordinates  (Battleship x y) = (x, y)

这不是毫无意义的风格,但很简单。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/27028986

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档