哈斯克尔中的三元运算符



我经常对可选值使用列表推导:

[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

正如你所看到的很多如果:)

所以我玩了一些运算符,并想出了这个语法:

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 运算符之外,还有没有语法来表达选择?也许有列表理解?

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

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

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

你可以重写你的例子:

lblTabX = bool 3 5 isAtBottom

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

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。 (希望,您发现Context是另一种唱片风格的类型,当然您可以玩同样的技巧)。

如果 TabConfig 有更多参数并且位于底部只会更改一些参数,您可以这样做

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

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

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

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

最新更新