有没有办法在保持流畅的管道语法的同时执行比较?

时间:2016-08-09 22:57:57

标签: f#

有没有办法在保持流畅的管道语法的同时执行比较?

例如,我想这样做:

(positions , source) ||> jump target
                      |> List.length = positions.Length || 
                                       positions.Length - 1

但相反,我觉得我必须这样做:

let updatedPositions = (positions , source) ||> jump target
// Return result
updatedPositions |> List.length = positions.Length       || 
updatedPositions |> List.length = positions.Length - 1

附录

[<Property(QuietOnSuccess = true, MaxTest=1000)>]
let ``Attempted checker jump results in zero or one distinct checkers removed`` () =

    // Setup
    gen {
        let! source =          Arb.generate<Piece>
        let! target =          Arb.generate<Piece>
        let! otherPositions =  Arb.generate<Space list>
        let! positions =       Occupied source :: Occupied target
                                               :: otherPositions 
                                               |> Gen.shuffle

        return source , target, positions |> Seq.distinct
                                          |> Seq.toList }
    |> Arb.fromGen

    // Test
    |> Prop.forAll 
    <| fun (source , target, positions) -> 
        (positions , source) ||> jump target
                              |> List.length = positions.Length ||
                                 List.length = positions.Length - 1 // Wish I could do this...

2 个答案:

答案 0 :(得分:6)

@ildjarn在评论中发布的解决方案是您问题的答案。

但是,我个人不会这样做。当您对捕获代码的主要数据的某些内容执行一系列转换时,管道非常有用。管道有一个输入和一个输出这个事实很好地捕获了这个:

let output = 
  input 
  |> firstTransformation
  |> secondTransformation

Pipe可以在许多其他场景中使用(毕竟,它只是一个操作符),但我认为在其他场景中使用它并不会使F#代码更具可读性。将管道仅用于&#34;主要数据&#34;是快速浏览一下代码,了解重要操作的位置。

这也是我对||>不太热衷的部分原因 - 它有助于fold的类型推断,但除此之外,它给人的假象是函数有一个&#34 ;主要输入&#34;事实上,它需要多个同样重要的投入。所以在你的例子中,我会写:

let updatedPositions = jump target positions source
updatedPositions.Length = positions.Length || 
  updatedPositions.Length = positions.Length - 1

我认为这更好地对应于代码背后的逻辑 - 将两件事传递给jump并检查结果的两个属性 - 而不是以下管道版本:

(positions, source) 
||> jump target
|> fun updatedPositions -> 
     updatedPositions.Length = positions.Length ||
     updatedPositions.Length = positions.Length - 1

let的版本更短,更直接。在阅读第二篇文章时,你必须解开很多东西来弄清楚发生了什么。

答案 1 :(得分:1)

对于代码高尔夫答案,如果要避免使用显式lambda函数:

如果您从Haskell flip定义let inline flip f a b = f b a运算符,那么您可以执行|> List.length |> flip List.contains [positions.Length; positions.Length - 1]

您可以通过将其定义为实际操作符来使其更加漂亮,以便它看起来像占位符:let inline (--) f a b = f b a然后您可以执行|> List.length |> (List.contains -- [positions.Length; positions.Length - 1])

或者您可以使用https://github.com/fsprojects/FSharp.Core.Fluent来简化它,它只会变成|> List.length |> [positions.Length; positions.Length - 1].contains

我不会为生产代码做任何这些。