supresion resueltos parentesis operaciones matematicos llaves fracciones ejercicios ejemplos ecuaciones corchetes con como combinadas scala syntax parentheses braces

resueltos - ¿Cuál es la diferencia formal en Scala entre llaves y paréntesis, y cuándo deben usarse?



operaciones con parentesis corchetes y llaves (8)

¿Cuál es la diferencia formal entre pasar argumentos a funciones entre paréntesis () y entre llaves {} ?

La sensación que obtuve del libro Programación en Scala es que Scala es bastante flexible y debería usar el que más me guste, pero creo que algunos casos se compilan y otros no.

Por ejemplo (solo significa un ejemplo; agradecería cualquier respuesta que aborde el caso general, no solo este ejemplo en particular):

val tupleList = List[(String, String)]() val filtered = tupleList.takeWhile( case (s1, s2) => s1 == s2 )

=> error: inicio ilegal de expresión simple

val filtered = tupleList.takeWhile{ case (s1, s2) => s1 == s2 }

=> bien.


Aquí hay un par de reglas e inferencias diferentes: en primer lugar, Scala deduce las llaves cuando un parámetro es una función, por ejemplo, en list.map(_ * 2) las llaves se deducen, es solo una forma más corta de list.map({_ * 2}) . En segundo lugar, Scala le permite omitir los paréntesis en la última lista de parámetros, si esa lista de parámetros tiene un parámetro y es una función, por lo que list.foldLeft(0)(_ + _) se puede escribir como list.foldLeft(0) { _ + _ } (o list.foldLeft(0)({_ + _}) si desea ser más explícito).

Sin embargo, si agrega un case , obtiene, como han mencionado otros, una función parcial en lugar de una función, y Scala no deducirá las llaves para funciones parciales, por lo que list.map(case x => x * 2) no funcionará , pero tanto list.map({case x => 2 * 2}) como list.map { case x => x * 2 } serán.


Con frenillos, tienes un punto y coma inducido para ti y paréntesis no. Considere la función takeWhile , ya que espera una función parcial, solo {case xxx => ??? } {case xxx => ??? } es una definición válida en lugar de paréntesis alrededor de la expresión del caso.


Creo que vale la pena explicar su uso en llamadas a funciones y por qué suceden varias cosas. Como alguien ya dijo, los frenillos definen un bloque de código, que también es una expresión, por lo que se puede colocar donde se espera la expresión y se evaluará. Cuando se evalúa, sus declaraciones se ejecutan y el último valor de la declaración es el resultado de una evaluación de bloque completo (algo así como en Ruby).

Teniendo eso podemos hacer cosas como:

2 + { 3 } // res: Int = 5 val x = { 4 } // res: x: Int = 4 List({1},{2},{3}) // res: List[Int] = List(1,2,3)

El último ejemplo es solo una llamada de función con tres parámetros, de los cuales cada uno se evalúa primero.

Ahora para ver cómo funciona con las llamadas a funciones, definamos una función simple que toma otra función como parámetro.

def foo(f: Int => Unit) = { println("Entering foo"); f(4) }

Para llamarlo, necesitamos pasar una función que toma un parámetro de tipo Int, para que podamos usar la función literal y pasarla a foo:

foo( x => println(x) )

Ahora, como se dijo antes, podemos usar el bloque de código en lugar de una expresión, así que vamos a usarlo

foo({ x => println(x) })

Lo que sucede aquí es que el código dentro de {} se evalúa, y el valor de la función se devuelve como un valor de la evaluación de bloque, luego este valor se pasa a foo. Esto es semánticamente igual a la llamada anterior.

Pero podemos añadir algo más:

foo({ println("Hey"); x => println(x) })

Ahora nuestro bloque de código contiene dos instrucciones, y como se evalúa antes de que se ejecute foo, lo que sucede es que primero se imprime "Hey", luego nuestra función se pasa a foo, se imprime "Entering foo" y finalmente se imprime "4" .

Aunque esto parece un poco feo y Scala nos permite omitir el paréntesis en este caso, por lo que podemos escribir:

foo { println("Hey"); x => println(x) }

o

foo { x => println(x) }

Eso se ve mucho mejor y es equivalente a los anteriores. Aquí, el bloque de código aún se evalúa primero y el resultado de la evaluación (que es x => println (x)) se pasa como un argumento a foo.


Debido a que está utilizando case y case , está definiendo una función parcial y las funciones parciales requieren llaves.


La comunidad hace un esfuerzo para estandarizar el uso de llaves y paréntesis, consulte la Guía de estilo de Scala (página 21): http://www.codecommit.com/scala-style-guide.pdf

La sintaxis recomendada para las llamadas de métodos de orden superior es usar siempre llaves y omitir el punto:

val filtered = tupleList takeWhile { case (s1, s2) => s1 == s2 }

Para las llamadas de método "normales" debe usar el punto y los paréntesis.

val result = myInstance.foo(5, "Hello")


No creo que haya nada particular o complejo sobre llaves en Scala. Para dominar el uso complejo de ellos en Scala, solo tenga en mente un par de cosas simples:

  1. las llaves forman un bloque de código, que se evalúa hasta la última línea de código (casi todos los idiomas hacen esto)
  2. una función si se desea puede generarse con el bloque de código (sigue la regla 1)
  3. Las llaves se pueden omitir para el código de una línea, excepto por una cláusula de caso (opción Scala)
  4. los paréntesis se pueden omitir en la llamada de función con el bloque de código como parámetro (opción de Scala)

Vamos a explicar un par de ejemplos según las tres reglas anteriores:

val tupleList = List[(String, String)]() // doesn''t compile, violates case clause requirement val filtered = tupleList.takeWhile( case (s1, s2) => s1 == s2 ) // block of code as a partial function and parentheses omission, // i.e. tupleList.takeWhile({ case (s1, s2) => s1 == s2 }) val filtered = tupleList.takeWhile{ case (s1, s2) => s1 == s2 } // curly braces omission, i.e. List(1, 2, 3).reduceLeft({_+_}) List(1, 2, 3).reduceLeft(_+_) // parentheses omission, i.e. List(1, 2, 3).reduceLeft({_+_}) List(1, 2, 3).reduceLeft{_+_} // not both though it compiles, because meaning totally changes due to precedence List(1, 2, 3).reduceLeft _+_ // res1: String => String = <function1> // curly braces omission, i.e. List(1, 2, 3).foldLeft(0)({_ + _}) List(1, 2, 3).foldLeft(0)(_ + _) // parentheses omission, i.e. List(1, 2, 3).foldLeft(0)({_ + _}) List(1, 2, 3).foldLeft(0){_ + _} // block of code and parentheses omission List(1, 2, 3).foldLeft {0} {_ + _} // not both though it compiles, because meaning totally changes due to precedence List(1, 2, 3).foldLeft(0) _ + _ // error: '';'' expected but integer literal found. List(1, 2, 3).foldLeft 0 (_ + _) def foo(f: Int => Unit) = { println("Entering foo"); f(4) } // block of code that just evaluates to a value of a function, and parentheses omission // i.e. foo({ println("Hey"); x => println(x) }) foo { println("Hey"); x => println(x) } // parentheses omission, i.e. f({x}) def f(x: Int): Int = f {x} // error: missing arguments for method f def f(x: Int): Int = f x


Una vez intenté escribir sobre esto, pero al final me rendí, ya que las reglas son un tanto difusas. Básicamente, tendrás que entenderlo.

Quizás es mejor concentrarse en dónde se pueden usar indistintamente las llaves y paréntesis: cuando se pasan parámetros a las llamadas a métodos. Puede reemplazar el paréntesis con llaves si, y solo si, el método espera un solo parámetro. Por ejemplo:

List(1, 2, 3).reduceLeft{_ + _} // valid, single Function2[Int,Int] parameter List{1, 2, 3}.reduceLeft(_ + _) // invalid, A* vararg parameter

Sin embargo, hay más que necesita saber para comprender mejor estas reglas.

Mayor compilación de cheques con parens

Los autores de Spray recomiendan los paréntesis redondos porque dan mayor control de compilación. Esto es especialmente importante para los DSL como Spray. Al usar parens le está diciendo al compilador que solo se le debe dar una sola línea; por lo tanto, si accidentalmente le das dos o más, se quejará. Ahora, este no es el caso con llaves: si, por ejemplo, olvida un operador en algún lugar, entonces su código se compilará y obtendrá resultados inesperados y, posiblemente, un error muy difícil de encontrar. A continuación se presenta una propuesta (ya que las expresiones son puras y al menos darán una advertencia), pero señala lo siguiente:

method { 1 + 2 3 } method( 1 + 2 3 )

La primera compila, la segunda da error: '')'' expected but integer literal found . El autor quiso escribir 1 + 2 + 3 .

Se podría argumentar que es similar para los métodos de múltiples parámetros con argumentos predeterminados; es imposible olvidar accidentalmente una coma para separar los parámetros cuando se usa parens.

Verbosidad

Una nota importante a menudo pasada por alto acerca de la verbosidad. El uso de llaves de rizo conduce inevitablemente a un código detallado, ya que la guía de estilo de Scala establece claramente que el cierre de llaves debe estar en su propia línea:

... la llave de cierre está en su propia línea inmediatamente después de la última línea de la función.

Muchos auto-reformadores, como en IntelliJ, realizarán automáticamente este cambio de formato. Así que trata de mantener el uso de paréntesis redondos cuando puedas.

Notación Infix

Cuando se utiliza la notación infijo, como List(1,2,3) indexOf (2) , puede omitir los paréntesis si solo hay un parámetro y escribirlo como List(1, 2, 3) indexOf 2 . Este no es el caso de la notación de puntos.

Tenga en cuenta también que cuando tiene un único parámetro que es una expresión de múltiples tokens, como x + 2 o a => a % 2 == 0 , debe usar paréntesis para indicar los límites de la expresión.

Tuplas

Debido a que puede omitir paréntesis a veces, a veces una tupla necesita paréntesis adicional como en ((1, 2)) , y algunas veces el paréntesis externo puede omitirse, como en (1, 2) . Esto puede causar confusión.

Función / Función parcial de los literales con case

Scala tiene una sintaxis para funciones y funciones parciales. Se parece a esto:

{ case pattern if guard => statements case pattern => statements }

Los únicos otros lugares donde puede usar declaraciones de case son las palabras clave de match y catch :

object match { case pattern if guard => statements case pattern => statements }

try { block } catch { case pattern if guard => statements case pattern => statements } finally { block }

No puede utilizar declaraciones de case en ningún otro contexto . Por lo tanto, si desea utilizar el case , necesita llaves. En caso de que se esté preguntando qué hace la distinción entre una función y una función parcial literal, la respuesta es: contexto. Si Scala espera una función, una función que obtienes. Si espera una función parcial, obtienes una función parcial. Si se esperan ambos, da un error de ambigüedad.

Expresiones y bloques

Los paréntesis se pueden utilizar para hacer subexpresiones. Se pueden usar llaves para hacer bloques de código (esta no es una función literal, así que ten cuidado de no usarla como una). Un bloque de código consta de varias declaraciones, cada una de las cuales puede ser una declaración de importación, una declaración o una expresión. Dice así:

{ import stuff._ statement ; // ; optional at the end of the line statement ; statement // not optional here var x = 0 // declaration while (x < 10) { x += 1 } // stuff (x % 5) + 1 // expression } ( expression )

Entonces, si necesita declaraciones, declaraciones múltiples, una import o algo por el estilo, necesita llaves. Y como una expresión es una declaración, los paréntesis pueden aparecer dentro de llaves. Pero lo interesante es que los bloques de código también son expresiones, por lo que puedes usarlos en cualquier lugar dentro de una expresión:

( { var x = 0; while (x < 10) { x += 1}; x } % 5) + 1

Entonces, como las expresiones son declaraciones y los bloques de códigos son expresiones, todo lo que sigue es válido:

1 // literal (1) // expression {1} // block of code ({1}) // expression with a block of code {(1)} // block of code with an expression ({(1)}) // you get the drift...

Donde no sean intercambiables.

Básicamente, no puede reemplazar {} con () o viceversa en ningún otro lugar. Por ejemplo:

while (x < 10) { x += 1 }

Esto no es una llamada de método, por lo que no puede escribirlo de otra manera. Bueno, puedes poner llaves dentro del paréntesis para la condition , así como usar paréntesis dentro de las llaves para el bloque de código:

while ({x < 10}) { (x += 1) }

Por lo tanto, espero que esto ayude.


Mayor compilación de cheques con parens

Los autores de Spray recomiendan que los paréntesis redondos den mayor control de compilación. Esto es especialmente importante para los DSL como Spray. Al usar parens le está diciendo al compilador que solo se le debe dar una sola línea, por lo tanto, si accidentalmente le dio dos o más, se quejará. Ahora, este no es el caso con llaves; si, por ejemplo, olvida un operador en algún lugar donde compilará su código, obtendrá resultados inesperados y, posiblemente, un error muy difícil de encontrar. A continuación se ha diseñado (ya que las expresiones son puras y al menos darán una advertencia), pero hace el punto

method { 1 + 2 3 } method( 1 + 2 3 )

La primera compila, la segunda da error: '')'' expected but integer literal found. El autor quiso escribir 1 + 2 + 3 .

Se podría argumentar que es similar para los métodos de múltiples parámetros con argumentos predeterminados; es imposible olvidar accidentalmente una coma para separar los parámetros cuando se usa parens.

Verbosidad

Una nota importante a menudo pasada por alto acerca de la verbosidad. El uso de llaves de rizo lleva inevitablemente a un código detallado, ya que la guía de estilo de Scala establece claramente que las llaves de cierre deben estar en su propia línea: http://docs.scala-lang.org/style/declarations.html "... la llave de cierre está en su propia línea inmediatamente después de la última línea de la función ". Muchos auto-reformadores, como en Intellij, realizarán automáticamente este cambio de formato. Así que trata de mantener el uso de paréntesis redondos cuando puedas. Por ejemplo, la List(1, 2, 3).reduceLeft{_ + _} convierte en:

List(1, 2, 3).reduceLeft { _ + _ }