2012-03-02 15 views
27

EDIT: Continuo a ottenere voti positivi qui. Solo per la cronaca, non penso più che questo sia importante. Non ne ho avuto bisogno da quando l'ho pubblicato.Scala - mutable (var) riferimento parametro parametro

Vorrei fare seguito a Scala ...

def save(srcPath: String, destPath: String) { 
    if (!destPath.endsWith('/')) 
     destPath += '/' 
    // do something 
} 

... ma non riesco a Allucinante destPath è un val. C'è un modo per dichiarare destPath come var?

Nota: ci sono domande simili ma in tutte OP si voleva solo modificare l'array.

Si prega di non consiglio seguente:

La modifica dei dettagli i parametri di input è spesso visto come un cattivo stile e lo rende più difficile di ragionare sul codice.

Penso che sia valido nella programmazione imperativa (Scala consente entrambi, giusto?) E aggiungendo qualcosa come tmpDestPath aggiungerebbe solo confusione.

MODIFICA: non fraintendere. So che le stringhe non sono modificabili e non desidero un riferimento come riferimento perché non desidero modificare i dati del chiamante. Voglio solo modificare il riferimento locale alla stringa che il chiamante mi ha fornito con la mia stringa (ad esempio orig + '/'). Voglio modificare tale valore solo nell'ambito del metodo corrente. Guarda, questo è perfettamente valido in Java:

void printPlusOne(int i) { 
    i++; 
    System.out.println("i is: " + i); 
    System.out.println("and now it's same: " + i); 
} 

non ho per creare nuova variabile e non devo calcolare i + 1 due volte.

+5

Dopo il chiarimento la risposta è: non è possibile. – Debilski

+2

Questo è quello che sospettavo. Ho intenzione di pubblicarlo su scala-dibattito. – woky

+0

Bene, la comunità di Scala non è davvero favorevole alla possibilità di modificare direttamente i parametri di funzione, sia per valore che per riferimento. Il ragionamento è identico a quello del perché a Scala manca anche qualcos'altro dal tuo esempio: l'operatore unario '++' per i tipi numerici. Tali cose sono il riflesso di uno stile di programmazione non funzionale, orientato agli effetti collaterali, che è qualcosa che Scala generalmente ti incoraggia ad evitare. Così com'è, se vuoi mutare ripetutamente un parametro di funzione, devi prima memorizzarlo in un 'var', il che rende le tue intenzioni più chiare, comunque! – Destin

risposta

9

La JVM non consente il riferimento pass-by dei puntatori agli oggetti (che è come si farebbe in C++), quindi non si può fare esattamente ciò che si desidera.

Una possibilità è quella di restituire il nuovo valore:

def save(srcPath: String, destPath: String): String = { 
    val newPath = (if (!destPath.endsWith("/")) destPath+'/' else destPath) 
    // do something 
    newPath 
} 

Un altro è quello di creare un wrapper:.

case class Mut[A](var value: A) {} 

def save(srcPath: String, destPath: Mut[String]) { 
    if (!destPath.value.endsWith("/")) destPath.value += '/' 
    // do something 
} 

cui gli utenti dovranno quindi utilizzare sul modo in (Naturalmente, saranno tentati di save("/here",Mut("/there")) che getterà via le modifiche, ma questo è sempre il caso con argomenti della funzione pass-by-reference.)


Modifica: ciò che stai proponendo è una delle maggiori fonti di confusione tra i programmatori non esperti. Cioè, quando modifichi l'argomento di una funzione, stai modificando una copia locale (pass-by-value) o l'originale (pass-by-reference)? Se lo non lo puoi nemmeno modificare, è abbastanza chiaro che qualsiasi cosa tu faccia è una copia locale.

Basta fare così.

val destWithSlash = destPath + (if (!destPath.endsWith("/")) "/" else "") 

Vale la pena la mancanza di confusione su ciò che sta effettivamente accadendo.

+1

Ciao. Grazie. Ho paura che tu abbia frainteso la mia domanda. L'ho aggiornato. – woky

+1

"La JVM non consente il riferimento pass-by dei puntatori agli oggetti" Questo punto richiede un chiarimento molto accurato. Quando si passa un primitivo in Java: stiamo davvero passando per valore. Ma, quando si passa un oggetto: si passa di fatto un riferimento a quell'oggetto (che possiamo anche affermare che è stato fatto: in base al valore). Per questo motivo, l'istruzione "Java passa solo per valore" causa in realtà molta confusione, ma in realtà tale asserzione richiede la comprensione che le primitive vengono passate per valore ei riferimenti oggetto * vengono passati per valore. –

1

Gli oggetti stringa sono immutabili in Scala (e Java).Le alternative a cui posso pensare sono:

  1. Restituisce la stringa del risultato come valore di ritorno.
  2. Invece di utilizzare un parametro String, utilizzare un StringBuffer o StringBuilder, che non sono immutabili.

Nel secondo scenario si dovrebbe avere qualcosa di simile:

def save(srcPath: String, destPath: StringBuilder) { 
    if (!destPath.toString().endsWith("/")) 
     destPath.append("/") 
    // do something 
    // 
} 

EDIT

Se ho capito bene, si desidera utilizzare l'argomento come una variabile locale. Non puoi, perché tutti gli argomenti del metodo sono val in Scala. L'unica cosa da fare è quello di copiarlo in una variabile locale prima:

def save(srcPath: String, destPath: String) { 
    var destP = destPath 
    if (!destP.endsWith("/")) 
     destP += "/" 
    // do something 
    // 
} 
+1

Ciao. Grazie. Ho paura che tu abbia frainteso la mia domanda. L'ho aggiornato. – woky

+0

Annuncio 2. Non un'opzione, è solo confusione. StringBuilder implica che costruirò una grande stringa. Perché fare "String -> StringBuilder -> String" se posso fare "String (-> forse new String)"? La tua soluzione è solo un modo per ovviare alla mancanza di conoscenza di Scala o alla limitazione di Scala. Non lo faresti in Java, vero? – woky

+0

Non ho detto che usare StringBuilder è elegante, ma se non vuoi restituire una stringa come valore di ritorno, devi usare un argomento di un tipo di stringa mutabile. In realtà è possibile utilizzare endsWith() direttamente su uno StringBuilder in Scala (non è necessario chiamare toString()). – Giorgio

24

Non è possibile.

Dovrai dichiarare un extra var (o usare uno stile più funzionale :-)).

esempio semplicistico:

def save(srcPath: String, destPath: String) { 
    val normalizedDestPath = 
     if (destPath.endsWith('/')) destPath 
     else destPath + '/' 
    // do something with normalizedDestPath 
} 
+0

Ho aggiunto un esempio. –

0

Ecco un paio di suggerimenti:

1) Aggiorna il tuo funzione un po '

def save(srcPath: String, destPath: String) { 
    var dp = destPath 
    if (!dp.endsWith('/')) 
    dp+= '/' 
    // do something, but with dp instead of destPath 
} 

2) Creazione di una funzione di utilità da utilizzare prima di chiamare Salva

def savedPath(path: String) = 
    if(path.endsWith("/")) 
    path 
    else 
    path + "/" 

//call your save method on some path 
val myDestPath = ... 
val srcPath = ... 
save(srcPath, savedPath(myDestPath)) 
0

No, non è consentito in Scala. Altri hanno descritto alcuni workaround di basso livello (tutti buoni), ma ne aggiungerò uno di livello superiore. Proprio per questo tipo di stringhe di normalizzazione, torno a un'estensione pimped a scala.String con metodi come suffisso, prefisso, removeSuffix e removePrefix. suffisso e prefisso aggiungono o anteporre una stringa a un'altra, a meno che il suffisso o il prefisso non siano già presenti. removeSuffix e removePrefix fanno l'ovvio, rimuovendo una stringa dalla fine o dall'inizio di un'altra, se è presente. Il tuo caso d'uso sarebbe scritto

val normalizedPath = destPath.addSuffix("/") 

Se fate un po 'di operazioni di analisi dei dati o di file, questi metodi sono così a portata di mano che non si creda che tu abbia mai fatto senza di loro.

+0

Hai controllato 'stripPrefix' e' stripSuffix' in 'StringLike'? Sembra che facciano già ciò che fanno 'removePrefix' e' removeSuffix'. –

3

Forse si potrebbe ottenere il sistema di tipi per fare il lavoro per voi, quindi non c'è nemmeno bisogno di preoccuparsi di aggiunta di una barra di volta in volta:

class SlashString(s: String) { 
    override val toString = if (s endsWith "/") s else s + "/" 
} 
implicit def toSlashString(s: String) = new SlashString(s) 

Ora non è necessario alcun codice tutto per cambiare l'ingresso String:

def save(srcPath: String, destPath: SlashString) { 
    printf("saving from %s to %s", srcPath, destPath) 
} 

val src: String = "abc" 
val dst: String = "xyz" 

scala> save(src, dst) 
saving from abc to xyz/ 

è vero, c'è un po 'di messa a punto all'inizio, ma questo sarà meno così con le classi implicite nella versione 2.10, e rimuove tutto il disordine dal metodo, che era di cosa eri preoccupato

0

So che questa è una vecchia questione, ma se si desidera solo per riutilizzare il nome di argomento forse:

def save(srcPath: String, destPath: String) { 
    ((destPath: String) => { 
    // do something 
    })(if (!destPath.endsWith('/')) destPath + '/' else destPath) 
} 
Problemi correlati