haskell的三元运营商

时间:2013-10-29 07:11:32

标签: haskell list-comprehension ternary-operator

我经常将列表理解用于可选值:

[Parent parent, Destination [DestPage currPage]] ++ [OpenChildren | parent == Bookmark 0]

但我不知道如何做出选择而不是可选值。

我的很多代码都是这样的:

let lblTabX         = if isAtBottom then 5 else 3
    lblTabY         = if isAtBottom then 3 else 60
    lblTabPosition  = Position left (if isAtBottom then bottom else top)
    lblTabWidth     = if isAtBottom then lblPageX - 60 else 20
    lblTabHeight    = if isAtBottom then 20 else pageHeight - 80
    lblTabMargin    = if isAtBottom then Margin 0 3 else Margin 3 0

正如你看到很多ifs:)

所以我和一些运营商一起玩,并提出了这种语法:

iif c l r = if c then l else r

infixl 8 <-/
(<-/) l c = iif c l

infixl 8 /->
(/->) = ($)

我喜欢上一个例子现在的样子:

let lblTabX         = 5 <-/ isAtBottom /-> 3
    lblTabY         = 3 <-/ isAtBottom /-> 60
    lblTabPosition  = Position left (bottom <-/ isAtBottom /-> top)
    lblTabWidth     = (lblPageX - 60) <-/ isAtBottom /-> 20
    lblTabHeight    = 20 <-/ isAtBottom /-> (pageHeight - 80)
    lblTabMargin    = Margin 0 3 <-/ isAtBottom /-> Margin 3 0

这当然是一个玩具的例子。我无意使用它。 但我只是好奇,除了if运算符之外还有一种表达选择的语法吗?也许有列表推导?

4 个答案:

答案 0 :(得分:9)

Haskell中的

if并不是非常漂亮,但这并不是他们很少使用的原因。更多的是因为通常有更优雅的语法替代方案!在你的例子中,我会考虑

let (                lblTabX, lblTabY, lblTabPosition, lblTabWidth, lblTabHeight,    lblTabMargin )
     | isAtBottom =( 5,       3,       bottom,         lblPageX-60, 20,              Margin 0 3   )
     | otherwise  =( 3,       60,      top,            20,          pageHeight - 80, Margin 3 0   )

或者,您可以在本地定义已部分评估的运算符:

let bottomCase/|/topCase | isAtBottom = bottomCase
                         | otherwise  = topCase
    lblTabY         =                      3 /|/ 60
    lblTabPosition  = Position left $ bottom /|/ top
    lblTabWidth     =        (lblPageX - 60) /|/ 20
    lblTabHeight    =                     20 /|/ (pageHeight - 80)
    lblTabMargin    =             Margin 0 3 /|/ Margin 3 0

您绝对不希望多次对isAtBottom进行检查,无论您使用哪种语法,冗余代码总是很糟糕。 但是当你只需要一个基于简单布尔值的单一决策时,我会坚持使用标准if而不是定义自定义运算符。

答案 1 :(得分:4)

哎呀,摆脱所有这些条件。你有一堆相关的设置,在一个上下文中将有一组值,在另一个上下文中有另一组。

data TabConfig = TabConfig { tabX, tabY, tabWidth, tabHeight :: Int,
                             tabPosition :: Position,
                             tabMargin :: Margin }

(您不必使用记录语法;我为了清晰起见而这样做)

现在,您只需要为您的函数提供相应的TabConfig,从中可以提取值。您可以提供defaultConfig功能...

defaultConfig :: Context -> TabConfig
defaultConfig c = TabConfig { 5, 3, 20, (pageHeight c) - 80,
                              Position left bottom, Margin 3 0 }

你也可以

bottomConfig :: Context -> TabConfig
bottomConfig c = TabConfig { 3, 60, (pageX c) - 60, 20, Position left top, Margin 0 3 }

然后,您只需在适当的上下文中提供相应的TabConfig。 (Hopefull,你得知Context是另一种记录风格的类型,当然你也可以用它来玩同样的技巧。)

如果TabConfig有更多参数并且位于底部只更改了一些参数,则可以执行

bottomConfig c = let def = defaultConfig c
                 in def { tabX = 3, tabY = 60, ...

只需为当前上下文构建正确的配置,并使其可用于您的功能。为什么您的表格绘制功能必须知道您用来决定它们在页面上的位置如何约束其尺寸的逻辑?当新条件施加额外限制时会发生什么?把它们混合起来很可怕。关注点的分离对功能编程至少与OO一样重要。 ķ

当然,我想要暗示的是你想要读者monad。但是阅读器monad是如此简单,以至于你不必知道你正在使用它,如果monad对你仍然是令人生畏的。

答案 2 :(得分:3)

Data.Bool的下一个(?)GHC中我们将有一个bool函数:

bool :: a -> a -> Bool -> a
bool f _ False = f
bool _ t True  = t

我们可以重写你的例子:

lblTabX = bool 3 5 isAtBottom

答案 3 :(得分:0)

模式匹配是选择的主要语法,其他大多数都以它的形式表达。 (if是根据等效的case表达式定义的。