2011-01-18 11 views
5

Sono nuovo in scala e sto provando a scrivere una funzione letterale che controlla se un intero dato è dispari o no. il mio primo tentativo è:come semplificare letteralmente la funzione di scala in questo modo?

val isOdd = (x:Int) => (x & 1) == 1

funziona benissimo, e, dal momento che il parametro X appare solo una volta all'interno di questa funzione letterale, sono tentato di utilizzare il "_" notazione per semplificare ulteriormente, come questo:

val isOdd = ((_:Int) & 1) == 1

tuttavia questa volta il compilatore si lamenta:

warning: comparing a fresh object using `==' will always yield false 
val isOdd = ((_:Int) & 1) == 1 

cosa significa questo avvertimento? perché il compilatore riconosce ((_ :Int) & 1) come oggetto fresco anziché un'operazione bit a bit che genera un valore? c'è un modo per scrivere questa funzione letterale usando la notazione "_"?

+0

Basta usare: 'val dispari =! anche (_: Int) ' –

risposta

20

Il problema è fondamentalmente che Scala ha bisogno di capire la differenza tra

val isOdd = ((_:Int) & 1) == 1 

dove desideri che tutto a destra del segno di uguale sia una lambda e

val result = collection.map(_ + 1) 

in cui si desidera solo la roba all'interno delle parentesi per essere una lambda

Scala ha deciso che quando si utilizza la sottolineatura per creare una lambda, che sta andando a prendere la parentesi più interne, come i confini di quel lambda. C'è un'eccezione: (_:Int) non conta come le parentesi più interne perché il suo scopo è solo quello di raggruppare la dichiarazione di tipo con il segnaposto _.

qui:

val isOdd = ((_:Int) & 1) == 1 
      ^^^^^^^^^^^^^^ 
      this is the lambda 

val result = collection.map(_ + 1) 
          ^^^^^^^ 
          this is the lambda 

val result = collection.map((_ + 1)/2) 
          ^^^^^^^^ 
          this is the lambda 
          and the compiler can't infer the type of the _ 

val result = somemap.map((_ + 1)/2 * _) 
         ^^^^^^^^ 
         this is an inner lambda with one parameter 
         and the compiler can't infer the type of the _ 
         ^^^^^^^^^^^^^^^^^ 
         this is an outer lambda with one parameter 

Quest'ultimo caso ti permette di fare cose come

_.map(_ + 1) 

e hanno che vengono tradotti in

x => x.map(y=> y + 1) 
+1

+1 per l'uso eccezionale della chiave di accento (e una buona risposta!) – Andy

+0

per chiarire le regole sul limite dell'espressione lambda! –

2

Cosa Scala sta facendo è questo:

  • vede ((_:Int) & 1) e crea un oggetto di tipo (Int) => Int, vale a dire, una funzione.
  • poi applica l'operatore di confronto per confrontare == tale funzione al valore 1

una funzione non è uguale al valore 1. Quindi il risultato è false, in modo che il codice è equivalente a:

val isOdd = false 

Che cosa si potrebbe fare è creare un'altra funzione anonima che comporti la parte == 1 del calcolo. Questo è brutto:

val isOdd = ((_: Int) & 1)(_: Int) == 1 

Ciò equivale alla più dettagliato (e forse più facile da capire):

val isOdd = (x: Int) => 1 == ((_: Int) & 1)(x) 
+5

Non consiglio nessuna di queste soluzioni - sono entrambe più complicate, ed entrambe creano un lambda extra e non necessario. Rimani con 'val isOdd = (x: Int) => (x & 1) == 1' –

+1

Sicuro! Ma l'OP ha chiesto specificamente se ci fosse un modo per scrivere il suo lambda "isOdd" usando solo "_". Non penso che stiamo discutendo di buon stile qui. –

+0

Ti sei perso 'val isOdd = ((_: ​​Int) e 1) e poi (_ == 1)'. :-) –

7

Ci si va:

val isOdd = ((_: Int) & 1) andThen (1 ==) 
+0

+1 per l'idea geniale della composizione della funzione :) –

10

Solo un po 'barare:

val isOdd = (_: Int) % 2 == 1 

:-)

+2

+1 Non è barare. In effetti, ritengo che l'uso di un '&' per questo è l'ottimizzazione prematura. La forma '%' è più chiara, e qualsiasi compilatore (o forse il JIT) degno di questo valore vedrà '% 2' e lo cambierà in' & 1' se è effettivamente più veloce in quell'ambiente operativo. –

+0

Concordo sul fatto che in generale dovremmo lasciare questo tipo di ottimizzazioni ai compilatori; ma qui in realtà voglio capire la logica dietro questa cosa lambda. di nuovo per chiarire la regola delle parentesi per quanto riguarda lambda :) –

+0

@KenBloom Vorrei sostenere che l'utilizzo dell'algoritmo migliore per un lavoro non è l'ottimizzazione prematura, è solo una buona programmazione, e mi stanco delle persone che usano questa giustificazione per scrivere codice lento (in generale, questo è un micro esempio in cui la differenza è probabilmente trascurabile). Il fatto che tu possa realizzarlo in modo più elegante con un altro meccanismo ha tutto a che fare con lo stile e nulla a che fare con l'ottimizzazione. – PlexQ

1

Un approccio diverso

val isOdd = (_:Int).&(1) == 1 
Problemi correlati