visual varias then studio sino sentencia entonces else ejemplos condiciones anidado f# functional-programming

f# - varias - ¿Qué es un reemplazo funcional para declaraciones if-then?



si... entonces-sino(if... then-else) (7)

He estado aprendiendo F # y programación funcional y tratando de hacer las cosas de manera funcional. Sin embargo, cuando se trata de reescribir algún código que ya había escrito en C #, me atasco en declaraciones simples de si-luego (aquellas que solo hacen algo, no devuelven un valor). Sé que puedes lograr esto en F #:

if expr then do ()

Sin embargo, pensé que este era un enfoque imperativo para la codificación. Tal vez no he aprendido lo suficiente sobre programación funcional, pero no me parece funcional. Pensé que el enfoque funcional era componer funciones y expresiones, no simplemente ejecutar afirmaciones una después de la otra, que es lo que parece ... si parece alentar.

Entonces, ¿me estoy perdiendo algo y si ... entonces está perfectamente bien en el mundo funcional? Si no, ¿cuál es el equivalente funcional de tal declaración? ¿Cómo podría tomar un if-then y ponerlo en funcionamiento?

Editar: Pude haber hecho una pregunta incorrecta (lo siento, todavía bastante nueva para la programación funcional): tomemos un ejemplo del mundo real que incluso me hizo preguntar:

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()

El cuerpo de eso si, entonces, no devuelve nada, pero no sé cómo podría hacerlo más funcional (si eso es posible). No conozco la técnica adecuada para minimizar el código imperativo. Dada la naturaleza de F #, es bastante fácil transportar mi C # directamente, pero estoy teniendo dificultades para hacerlo funcionar. Cada vez que llego a tal afirmación en C #, y estoy tratando de transportarla a F #, me desalienta que no se me ocurra una manera de hacer que el código sea más funcional.


Hay dos observaciones que pueden ayudar en la transición de la programación imperativa a la funcional ("todo es una expresión"):

  1. unit es un valor, mientras que una expresión que devuelve void en C # se trata como una declaración. Es decir, C # hace una distinción entre enunciados y expresiones. En F #, todo es una expresión.

  2. En C # los valores pueden ser ignorados; en F # no pueden, por lo tanto, proporciona un mayor nivel de seguridad de tipo. Esta explicitud hace que los programas F # sean más fáciles de razonar y proporciona mayores garantías.


Mis disculpas por no saber F #, pero he aquí una solución posible en 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 } }}

La función $ if devuelve un objeto con la construcción if..then..el..elsif, utilizando el constructor de condiciones. Una vez que llame a Condition.elsif () creará otro objeto Condition, esencialmente creando una lista enlazada que se puede recorrer recursivamente usando sequence ()

Podrías usarlo así:

var eq = val => x => x == val ? true : false; $if( eq(128) ).then( doStuff ).else( doStuff ) .elsif( eq(255) ).then( doStuff ).else( doStuff ).call();

Sin embargo, me doy cuenta de que usar un objeto no es un enfoque puramente funcional. Entonces, en ese caso, podrían renunciar al objeto todos juntos:

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

Puedes ver que la magia está realmente en la función de secuencia (). No intentaré responder a tu pregunta específica en javascript. Pero creo que el punto principal es que debe hacer una función que ejecutará funciones arbitrarias bajo una instrucción if ... then, luego anidará múltiplos de esa función usando recursión para crear una cadena lógica compleja. Al menos de esta manera no tendrás que repetirte a ti mismo;)

Este código es solo un prototipo, así que tómelo como prueba de concepto y avíseme si encuentra algún error.


No es la expresión if lo que es imperativo, es lo que entra en la expresión if. Por ejemplo, let abs num = if num < 0 then -num else num sea ​​una forma totalmente funcional de escribir la función abs. Toma un argumento y devuelve una transformación de ese argumento sin efectos secundarios. Pero cuando tienes un "código que solo hace algo, no devuelves un valor", entonces estás escribiendo algo que no es puramente funcional. El objetivo de la programación funcional es minimizar la parte de su programa que se puede describir de esa manera. Cómo escribes tus condicionales es tangencial.


No hay nada de malo con si, entonces, en el mundo funcional.

Su ejemplo es realmente similar a let _ = expr ya que expr tiene efectos secundarios e ignoramos su valor de retorno. Un ejemplo más interesante es:

if cond then expr

que es equivalente a:

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

si usamos la coincidencia de patrones

Cuando la condición es simple o solo hay una expresión condicional, if-then es más legible que la coincidencia de patrones. Además, vale la pena observar que todo en la programación funcional es expresión. Entonces if cond then expr es en realidad el acceso directo de if cond then expr else () .

Si-entonces, en sí mismo no es imperativo, usar if-then como una declaración es una forma imperativa de pensar. Según mi experiencia, la programación funcional se trata más de la forma de pensar que de los flujos concretos de control en los lenguajes de programación.

EDITAR:

Tu código es totalmente legible. Algunos puntos menores son deshacerse de las palabras clave redundantes do , type annotation y postStream.Dispose() (usando use keyword):

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()


Para escribir código complejo, necesita ramificarse en algún punto. Hay un número muy limitado de formas en que puede hacerlo, y todas requieren un flujo lógico a través de una sección de código. Si desea evitar usar if / then / else, es posible hacer trampa con loop / while / repeat, pero eso hará que su código sea mucho menos razonable de mantener y leer.

La programación funcional no significa que no deba ejecutar sentencias una tras otra, simplemente significa que no debe tener un estado mutable. Cada función necesita comportarse confiablemente de la misma manera cada vez que se la llama. Cualquier diferencia en cómo se manejan los datos debe ser contabilizada por los datos que se pasan, en lugar de un disparador que esté oculto de lo que sea que llame a la función.

Por ejemplo, si tenemos una función foo(int, bool) que devuelve algo diferente dependiendo de si bool es verdadero o falso, es casi seguro que habrá una declaración if en algún lugar de foo() . Eso es perfectamente legítimo. Lo que NO es legítimo es tener una función foo(int) que devuelve algo diferente dependiendo de si es o no la primera vez que se llama en el programa. Esa es una función ''con estado'' y hace que la vida sea difícil para cualquiera que mantenga el programa.


Se considera funcional si su declaración if tiene un valor de retorno y no tiene efectos secundarios.

Supongamos que quiere escribir el equivalente de:

if(x > 3) n = 3; else n = x;

En lugar de hacer eso, usa la declaración return del comando if:

let n = (if x > 3 then 3 else x)

Este hipotético if es repentinamente funcional porque no tiene efectos secundarios; solo devuelve un valor. Piénselo como si fuera el operador ternario en algunos idiomas: int n = x>3?3:x;


Un punto importante que no se ha mencionado hasta ahora es la diferencia entre if .. then .. else y if .. then sin la rama else .

Si en idiomas funcionales

La interpretación funcional de if es que es una expresión que evalúa a algún valor. Para evaluar el valor de if c then e1 else e2 , evalúa la condición c y luego evalúa e1 o e2 , dependiendo de la condición. Esto le da el resultado del if .. then .. else .

Si tiene simplemente if c then e , entonces no sabe cuál será el resultado de la evaluación si c es false , porque no hay ninguna else rama. Lo siguiente claramente no tiene sentido:

let num = if input > 0 then 10

En F #, las expresiones que tienen efectos secundarios como printf "hi" devuelven un valor especial de tipo unit . El tipo tiene solo un valor (escrito como () ) y, por lo tanto, puede escribir if hace un efecto en un solo caso:

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

Esto siempre se evalúa como unit , pero en la rama true , también realiza el efecto secundario. En la rama false , simplemente devuelve un valor unit . En F #, no tiene que escribir el bit else () mano, pero conceptualmente, todavía está allí. Puedes escribir:

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

En cuanto a tu ejemplo adicional

El código se ve perfectamente bien para mí. Cuando tiene que lidiar con la API que es imprescindible (como muchas de las bibliotecas .NET), entonces la mejor opción es usar las funciones imperativas como if tuviera una sucursal de recuperación de unit .

Puede usar varios ajustes, como representar sus datos usando la option<string> (en lugar de simplemente string con string null o vacía). De esta forma, puede usar None para representar datos faltantes y cualquier otra cosa sería una entrada válida. Luego puede usar algunas funciones de orden superior para trabajar con opciones, como Option.iter , que llama a una función dada si hay un valor:

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) )

Esto no es realmente menos imperativo, pero es más declarativo, porque no tiene que escribir el if mismo. Por cierto: también recomiendo usar el use si quieres eliminar el objeto de forma automática.