6

I often use list comprehension for optional values:

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

But i do not know how to do a choice instead of optional value.

A lot of my code looks like this:

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

As you see a lot of ifs :)

So i was playing with some operators and came up with this syntax:

iif c l r = if c then l else r

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

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

And i like how the previous example now looks:

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

This is a toy example of course. I have no intention of using it. But i was just curious, is there a syntax to express the choice besides the if operator? Maybe with list comprehensions?

4

4 回答 4

10

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

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而不是定义自定义运算符。

于 2013-10-29T10:42:57.483 回答
5

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

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 中一样重要。ķ

当然,我想暗示的是你想要 Reader monad。但是 Reader monad 非常简单,如果 monad 仍然让您望而生畏,您不必知道自己正在使用它。

于 2013-10-29T14:12:04.397 回答
4

在 next(?) GHC 中,Data.Bool我们将有一个bool函数:

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

你的例子我们可以重写:

lblTabX = bool 3 5 isAtBottom
于 2013-10-29T08:16:54.447 回答
0

模式匹配是选择的主要语法,大多数其他东西都是用它来表达的。(例如if根据等价表达式定义)。case

于 2013-10-29T10:42:21.563 回答