f#-什么是if-then语句的功能替代品?

我一直在学习F#和函数式编程,并尝试以函数式方式进行操作。 但是,当涉及到重写某些我已经用C#编写的代码时,我陷入了简单的if-then语句中(仅执行某些操作而不返回值的语句)。 我知道您可以在F#中实现:

if expr then do ()

但是,我认为这是一种强制性的编码方法吗? 也许我对函数式编程还没有足够的了解,但是对我来说似乎并不起作用。 我认为功能方法是组成函数和表达式,而不是简单地一个接一个地执行语句,这是if-then所鼓励的。

那么,我是否错过了某些内容,如果-那么在功能世界中完全可以吗? 如果不是,那么此语句的功能等效项是什么? 我该如何进行if-then然后将其启用?

编辑:我可能问了一个错误的问题(对不起,对于函数式编程还是很新的):让我们举一个现实的例子,让我什至问这个问题:

if not <| System.String.IsNullOrWhiteSpace(data) then do
    let byteData = System.Text.Encoding.Unicode.GetBytes(data)
    req.ContentLength <- int64 byteData.Length
    let postStream : System.IO.Stream = req.GetRequestStream()
    postStream.Write(byteData, 0, byteData.Length)
    postStream.Flush()
    postStream.Dispose()

if-then的主体不返回任何内容,但是我不知道如何使它更具功能性(如果可能的话)。 我不知道最小化命令性代码的正确技术。 鉴于F#的性质,直接传输C#非常容易,但是我很难将其启用。 每当我在C#中遇到这样的if语句,并且试图将其传输到F#时,我都会灰心,因为我想不出一种使代码更具功能性的方法。

Bob asked 2020-07-23T14:50:07Z
7个解决方案
35 votes

到目前为止,尚未提及的重要一点是ifuse之间(没有Dispose)分支的区别。

if功能语言

if的功能解释是它是一个评估为一定值的表达式。 要评估use的值,请评估条件Dispose,然后根据条件评估NoneOption.iter。 这将为您提供if .. then .. else的结果。

如果只有if,那么您不知道如果useDispose,则评估结果应该是什么,因为没有None! 以下内容显然没有意义:

let num = if input > 0 then 10

在F#中,具有副作用的表达式(例如if)返回类型use的特殊值。该类型只有一个值(写为Dispose),因此您可以编写None,此操作仅在一种情况下起作用:

let u = if input > 0 then printf "hi" else ()

它的总值为2979266390617752752576,但在use分支中,它也具有副作用。 在Dispose分支中,它仅返回None值。 在F#中,您不必手动编写Option.iter,但从概念上讲,它仍然存在。 你可以写:

let u = if input > 0 then printfn "hi"

关于您的其他示例

该代码对我来说似乎很好。 当您必须处理命令性的API(如许多.NET库)时,最好的选择是将命令性的功能(如if)与use返回分支一起使用。

您可以使用各种调整,例如使用if表示数据(而不是仅使用DisposeDispose或空字符串表示数据)。 这样,您可以使用None表示丢失的数据,其他任何内容都是有效的输入。 然后,您可以使用一些高阶函数来处理选项,例如Option.iter,如果有值,该函数将调用给定函数:

maybeData |> Option.iter (fun data ->
    let byteData = System.Text.Encoding.Unicode.GetBytes(data)  
    req.ContentLength <- int64 byteData.Length  
    use postStream = req.GetRequestStream()  
    postStream.Write(byteData, 0, byteData.Length) )

这并不是真正的必要,而是更具说明性的,因为您不必自己编写if。 顺便说一句:如果您想自动Dispose对象,我也建议使用use

Tomas Petricek answered 2020-07-23T14:51:02Z
14 votes

在功能世界中,if-then并没有错。

您的示例实际上类似于let _ = expr,因为do具有副作用,我们将忽略其返回值。 一个更有趣的示例是:

if cond then expr

等效于:

match cond with
| true -> expr
| false -> ()

如果我们使用模式匹配。

当条件简单或只有一个条件表达式时,if-then比模式匹配更具可读性。 此外,值得注意的是,函数式编程中的所有内容都是表达式。 因此do实际上是postStream.Dispose()的快捷方式。

如果-那么本身不是必须的,使用-那么作为陈述是必须的思维方式。 根据我的经验,函数式编程更多是关于思维方式的,而不是编程语言中的具体控制流程。

编辑:

您的代码是完全可读的。 一些次要点摆脱了多余的do关键字,类型注释和postStream.Dispose()(通过使用use关键字):

if not <| System.String.IsNullOrWhiteSpace(data) then
    let byteData = System.Text.Encoding.Unicode.GetBytes(data)
    req.ContentLength <- int64 byteData.Length
    use postStream = req.GetRequestStream()
    postStream.Write(byteData, 0, byteData.Length)
    postStream.Flush()
pad answered 2020-07-23T14:51:56Z
10 votes

并非必须的if表达式,而是if表达式中的内容。 例如,let abs num = if num < 0 then -num else num是编写abs函数的一种完全功能性的方法。 它接受一个参数,并返回该参数的转换而没有副作用。 但是,当您拥有“只做某事而不返回值的代码”时,那么您正在编写的东西并不是纯粹的功能。 函数式编程的目的是最大程度地减少程序中可以用这种方式描述的部分。 您如何编写条件句是切线的。

Chuck answered 2020-07-23T14:52:16Z
8 votes

为了编写复杂的代码,您需要在某个时候分支。 您可以采用的方法数量非常有限,而且所有这些方法都需要通过一段代码进行逻辑流程。 如果您想避免使用if / then / else,则可以使用loop / while / repeat作弊-但这会使您的代码难以维护和读取。

函数式编程并不意味着您不应该一个接一个地执行语句-它只是意味着您不应该具有可变状态。 每个函数每次调用都必须可靠地以相同的方式运行。 数据处理方式的任何差异都需要通过传入的数据来解决,而不是通过任何调用该函数的方法隐藏的触发器来解决。

例如,如果我们有一个函数foo(int, bool),它返回的内容取决于bool是true还是false,则几乎可以肯定在foo()的某处有if语句。这是完全合法的。 不合法的是拥有一个函数foo(int),该函数根据是否是在程序中首次调用而返回不同的内容。 这是一个“有状态”功能,它使任何维护该程序的人都很难过。

Michael Martin answered 2020-07-23T14:52:47Z
6 votes

如果您的if语句具有返回值且没有副作用,则认为该函数起作用。

假设您要编写相当于以下内容的内容:

if

您可以使用if命令中的return语句来代替这样做:

if

假设if突然起作用,因为它没有副作用。 它只返回一个值。 认为它是某些语言中的三元运算符:int n = x>3?3:x;

Tim answered 2020-07-23T14:53:24Z
3 votes

有两种观察可以帮助从命令式编程过渡到功能性编程(“一切都是表达”):

  1. unit是一个值,而在C#中返回void的表达式被视为语句。 也就是说,C#区分了语句和表达式。 在F#中,一切都是表达式。

  2. 在C#中,可以忽略值; 在F#中它们不能,因此提供了更高级别的类型安全性。 这种明确性使F#程序更易于推理,并提供了更大的保证。

Daniel answered 2020-07-23T14:53:53Z
1 votes

抱歉,我不认识F#,但这是JavaScript中的一种可能的解决方案:

function $if(param) {
    return new Condition(param)
}

function Condition(IF, THEN, ELSE) {
    this.call = function(seq) {
        if(this.lastCond != undefined) 
            return this.lastCond.call(
                sequence(
                    this.if, 
                    this.then, 
                    this.else, 
                    (this.elsif ? this.elsif.if : undefined),
                    seq || undefined
                )
            );
         else 
            return sequence(
                this.if, 
                this.then, 
                this.else, 
                (this.elsif ? this.elsif.if : undefined),
                seq || undefined
            )
    }


    this.if   = IF ? IF : f => { this.if = f; return this };
    this.then = THEN ? THEN : f => { this.then = f; return this };
    this.else = ELSE ? ELSE : f => { this.else = f; return this };
    this.elsif = f => {this.elsif = $if(f); this.elsif.lastCond = this; return this.elsif};
}

function sequence(IF, THEN, ELSE, ELSIF, FINALLY) {
    return function(val) {
        if( IF(val) ) 
            return THEN();

        else if( ELSIF && ELSIF(val) ) 
            return FINALLY(val);

        else if( ELSE ) 
            return ELSE();

        else 
            return undefined

    }
}}

$ if函数使用Condition构造函数返回带有if..then..else..elsif构造的对象。调用Condition.elsif()之后,您将创建另一个Condition对象-本质上是创建一个链表,该链表可以使用sequence()进行递归遍历

您可以像这样使用它:

var eq = val => x => x == val ? true : false;

$if( eq(128) ).then( doStuff ).else( doStuff )
.elsif( eq(255) ).then( doStuff ).else( doStuff ).call();

但是,我意识到使用对象不是纯粹的功能方法。 因此,在这种情况下,您可以一起放弃对象:

sequence(f, f, f, f,
    sequence(f, f, f, f
        sequence(f, f, f)
    )
);

您会看到魔术确实在sequence()函数中。我不会尝试用javascript回答您的特定问题。 但我认为主要要点是,您应该使一个函数在if..then语句下运行任意函数,然后使用递归嵌套该函数的倍数以创建复杂的逻辑链。 至少通过这种方式,您将不必重复自己;)

这段代码只是一个原型,因此请以它作为概念验证,如果您发现任何错误,请告诉我。

Duco answered 2020-07-23T14:54:36Z
translate from https://stackoverflow.com:/questions/9298419/whats-a-functional-replacement-for-if-then-statements