Vag*_*rdi 6 haskell list-comprehension ternary-operator
我经常使用list comprehension来表示可选值:
[Parent parent, Destination [DestPage currPage]] ++ [OpenChildren | parent == Bookmark 0]
Run Code Online (Sandbox Code Playgroud)
但我不知道如何做出选择而不是可选值.
我的很多代码看起来像这样:
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
Run Code Online (Sandbox Code Playgroud)
正如你看到很多ifs :)
所以我正在玩一些运算符,并提出了这种语法:
iif c l r = if c then l else r
infixl 8 <-/
(<-/) l c = iif c l
infixl 8 /->
(/->) = ($)
Run Code Online (Sandbox Code Playgroud)
我喜欢上一个例子现在的样子:
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
Run Code Online (Sandbox Code Playgroud)
这当然是一个玩具的例子.我无意使用它.但我只是好奇,除了if运算符之外还有一种表达选择的语法吗?也许有列表推导?
if在Haskell的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 )
Run Code Online (Sandbox Code Playgroud)
或者,您可以在本地定义已部分评估的运算符:
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
Run Code Online (Sandbox Code Playgroud)
你肯定不想isAtBottom多次检查,冗余代码总是坏的,无论你使用哪种语法.但是当你只需要一个基于简单布尔值的单一决策时,我会坚持标准if而不是定义自定义运算符.
在下一个(?)GHC中Data.Bool我们将有一个bool函数:
bool :: a -> a -> Bool -> a
bool f _ False = f
bool _ t True = t
Run Code Online (Sandbox Code Playgroud)
我们可以重写你的例子:
lblTabX = bool 3 5 isAtBottom
Run Code Online (Sandbox Code Playgroud)
哇,摆脱所有这些条件。你有一堆相关的设置,在一个上下文中会有一组值,在另一个上下文中会有另一组值。
data TabConfig = TabConfig { tabX, tabY, tabWidth, tabHeight :: Int,
tabPosition :: Position,
tabMargin :: Margin }
Run Code Online (Sandbox Code Playgroud)
(您不必使用记录语法;我这样做是为了清楚起见)
现在您只需要为您的函数提供适当的TabConfig,它们就可以从中提取值。你可以提供一个defaultConfig函数...
defaultConfig :: Context -> TabConfig
defaultConfig c = TabConfig { 5, 3, 20, (pageHeight c) - 80,
Position left bottom, Margin 3 0 }
Run Code Online (Sandbox Code Playgroud)
你也可以拥有
bottomConfig :: Context -> TabConfig
bottomConfig c = TabConfig { 3, 60, (pageX c) - 60, 20, Position left top, Margin 0 3 }
Run Code Online (Sandbox Code Playgroud)
然后您只需在适当的上下文中提供适当的 TabConfig。(希望你发现这Context是另一种唱片风格的类型,当然你可以用它玩同样的技巧)。
如果 TabConfig 有更多参数并且在底部只更改一些,您可以这样做
bottomConfig c = let def = defaultConfig c
in def { tabX = 3, tabY = 60, ...
Run Code Online (Sandbox Code Playgroud)
只需为当前上下文构建正确的配置并使其可用于您的功能。为什么你的表格绘制函数必须知道你用来决定它们在页面上的位置如何约束它们的尺寸的逻辑?当新条件施加额外约束时会发生什么?把它们混在一起太可怕了。关注点分离对于函数式编程至少与面向对象一样重要。钾
当然,我想暗示的是你想要 Reader monad。但是 Reader monad 非常简单,如果 monad 对您来说仍然令人生畏,那么您不必知道您正在使用它。