2011-12-16 88 views
7

我來自Groovy,它對每個接受單參數閉包的類型都有一個.with方法;參數是調用.with方法的對象。這允許擴展功能鏈功能的非常酷的技術,它釋放你引入臨時變量的責任,考慮代碼的因素,使其更易於閱讀和執行其他細節。.with Scala中的替代方法

我希望能夠做這樣的事情:

Seq(1, 2, 3, 4, 5) 
    .filter(_ % 2 == 0) 
    .with(it => if (!it.isEmpty) println(it)) 

代替

val yetAnotherMeaninglessNameForTemporaryVariable = 
    Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0) 
if (!yetAnotherMeaninglessNameForTemporaryVariable.isEmpty) 
    println(yetAnotherMeaninglessNameForTemporaryVariable) 

在第一個例子換句話說,.with是有點兒類似於.foreach但不是迭代通它在對象本身上被調用一次的對象的項目。所以it等於Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0)

因爲我很驚訝沒有發現這樣的事情在Scala中,我的問題是:

  • 我這麼想嗎?
  • 有沒有其他Scala原生的技術?
  • 如果沒有,是否有任何體面的原因,爲什麼這個功能沒有在Scala中實現?

更新: 一個相應的功能要求已經被張貼在斯卡拉問題跟蹤:https://issues.scala-lang.org/browse/SI-5324。請投票贊成

+1

只需要注意一點:`with`是Scala中的一個保留字,因此該方法無法命名爲相同的東西。它仍然應該以其他名字存在;這是StackOverflow上最常見的Scala問題和答案,據我所知(「它不存在;讓你自己像這樣」)! – 2011-12-16 18:59:09

+0

我認爲`convert`這個名字最適合,因此表明這種方法應該沒有副作用,並且因爲它將調用者作爲參數並返回新的東西,它必須是某種轉換。從這個意義上說,這個功能在標準庫中是不可替代的。同樣如http://stackoverflow.com/a/8538277/485115中建議的那樣,還應該有一個名爲「tap」的副作用變體,它返回調用者對象。 – 2011-12-16 23:15:15

回答

12

標準庫中不存在任何此類方法,但不難定義自己的方法。

implicit def aW[A](a: A) = new AW(a) 
class AW[A](a: A) { 
    def tap[U](f: A => U): A = { 
    f(a) 
    a 
    } 
} 

val seq = Seq(2, 3, 11). 
      map(_ * 3).tap(x => println("After mapping: " + x)). 
      filter(_ % 2 != 0).tap(x => println("After filtering: " + x)) 

編輯:(響應評論)

哦,我誤解了。你需要的是斯卡拉茲圖書館。名稱爲|>(簡稱管道運營商)。就這樣,你的例子看起來如下所示:

Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0) |> { it => if(!it.isEmpty) println(it) } 

如果您不能使用Scalaz,你可以自己定義操作符:

implicit def aW[A](a: A) = new AW(a) 
class AW[A](a: A) { 
    def |>[B](f: A => B): B = f(a) 
} 

而且這不是一個不好的做法,皮條客有用的方法(s)在現有的類型。您應該謹慎使用隱式轉換,但我認爲這兩個組合器對於他們的皮條客來說是足夠普遍的,這是合理的。

+0

是的我知道我可以擴展標準庫implicits,但我只是發現脫離標準一個不好的做法。這就是爲什麼我的問題是爲什麼Scala的人不這樣做,因爲可能這應該發佈在他們的問題跟蹤器上。 除了`tap`函數,雖然非常有用,但它與`with`有點不同,它不是返回結果而是返回對象本身。 在標準庫IMO中同時具有`.with`和`.tap`將會非常酷。 – 2011-12-16 18:29:24

3

嘗試某事像這樣描述

object Test { 
def main(args: Array[String]) { 
    delayed(time()); 
} 

def time() = { 
    println("Getting time in nano seconds") 
    System.nanoTime 
} 

def delayed(t: => Long) = { 
    println("In delayed method") 
    println("Param: " + t) 
    t 
} 
} 

println(Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0).ensuring(!_.isEmpty)) 

如果條件不符合,則會引發斷言異常。

+0

+1,但它僅適用於單個語句執行(例如,此處引用的println)。如果你想做多個操作,它會變得棘手。 – aishwarya 2011-12-16 18:04:45

+0

用於其他目的的好技術。對於這一個拋出一個例外是過度殺傷 – 2011-12-16 18:17:32

7

沒有爲包含在斯卡拉這種模式的一些語法:

Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0) match { case it => if (!it.isEmpty) println(it) } 

然而,這是沒有公認的習慣用法,這樣你應該從(AB),使用它可能避免。

如果你不喜歡發明了虛擬變量名稱的負載和負載,記住,你可以使用範圍括號:

val importantResult = { 
    val it = Seq(1,2,3).filter(_ % 2 == 0) 
    if (!it.isEmpty) println(it) 
    it 
} 

val otherImportantResultWithASpeakingVariableName = { 
    val it = // ... 
    /* ... */ 
    it 
} 
1

雖然我很喜歡其他解決方案更好(因爲他們是更當地化,因此更容易執行) ,不要忘記,你可以

{ val x = Seq(1,2,3,4,5).filter(_ % 2 == 0); println(x); x } 

爲了避免你的無意義變量名稱衝突,並保持它們限制在適當的範圍。

1

這僅僅是功能應用f(x)翻轉發揮得淋漓盡致:x.with(f) ...如果你正在尋找斯卡拉做with一個地道的表達方式,取消其翻轉:

(it => if (!it.isEmpty) println(it)) (Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0)) 

同樣,如果您想要x.with(f).with(g),只需使用g(f(x)) ...