是否可以使用Scala模式匹配重寫下面的代碼?Scala模式匹配:如何匹配列表中的元素?
val ls: List[String] = ??? // some list of strings
val res = if (ls.contains("foo")) FOO
else if (ls.contains("bar")) BAR
else SOMETHING_ELSE
是否可以使用Scala模式匹配重寫下面的代碼?Scala模式匹配:如何匹配列表中的元素?
val ls: List[String] = ??? // some list of strings
val res = if (ls.contains("foo")) FOO
else if (ls.contains("bar")) BAR
else SOMETHING_ELSE
您可以實現這使用功能如
def onContains[T](xs: Seq[String], actionMappings: (String, T)*): Option[T] = {
actionMappings collectFirst {
case (str, v) if xs contains str => v
}
}
而且使用這樣的:
val x = onContains(items,
"foo" -> FOO,
"bar" -> BAR
)
感謝您的回答。 'actionMappings:(String,T)*'中的*意味着什麼? –
它允許定義一個方法有一個可變數量的參數,可以作爲一個'Seq'集合來訪問。 http://www.tutorialspoint.com/scala/functions_variable_arguments.htm –
您可以添加if
條件匹配這樣的:
ls match {
case x if x.contains("foo") => // FOO
case x if x.contains("bar") => // BAR
case _ => // ELSE
}
然而,這是不是最好的方式,因爲每個if
檢查需要遍歷列表,所以這並不能很好地擴展。有許多不同的方法可以解決這個問題,但我們需要了解更多關於內涵的信息,因爲運行時語義通常與您的代碼不同(例如,您可以遞歸遍歷列表尋找「foo」或「酒吧「,但假設你只有一個在列表中)。
我以爲這樣做,但'案件'看起來多餘,因爲我有'如果'無論如何。 我想寫一些像{case _ ::「foo」:: _ => ??? } – wajda
正如弗蘭克的回答所說,這是可能的,但如果你這樣做會很骯髒,那麼代價很高。
這取決於你想要做什麼。你想返回那個「foo」或「bar」的索引(例如)?然後你會這樣做:
def indexOf[T]: (List[T], T) => Int = (ls, x) => ls match {
case Nil => -1
case e::es if(e.equals(x)) => 0
case e::es => val i = indexOf(es, x); if(i < 0) i else i + 1
}
此代碼未經測試,但您明白了。
如果你需要的是某種具有優先命令執行的我可以建議
def executeCommand(input: List[String]): Option[Unit] = {
val priorities = Map(
"foo" -> 1,
"bar" -> 2,
"baz" -> 3) withDefault(_ => 4)
def extractCommand(cmds: List[String]): Option[String] =
(cmds sortBy priorities).headOption
extractCommand(input) map {
case "foo" => println("found foo")
case "bar" => println("found bar")
case "baz" => println("found baz")
case _ => println("no known command")
}
}
在這種具體實施沒有意義的結果被返回(你只能去副作用),但如果你的情況下,應該返回一些值,你會發現它包裹在一個Option
作爲方法的結果。
修訂
根據您的附加評論
def execute(input: List[String]): Option[String] = {
val commands: PartialFunction[String, String] = {
case "foo" => "result for foo"
case "bar" => "result for bar"
case "baz" => "result for baz"
}
(input find commands.isDefinedAt) map commands
}
這隻有你的命令是互斥的,只有一個應在input
列表
是的,我的案例應該返回一個值(請參閱我對該問題的評論)。我喜歡你的方法,儘管它比簡單的if-else構造簡潔得多。但我需要將我的常量(foo,bar,baz等)在代碼中列出兩次。我有幾十個他們,所以我會避免這一點。 – wajda
val ls = List[String]("bar", "foo", "baz") // stuff to check against
val mappy = Map[String, String]("foo" -> "FOO", "bar" -> "BAR") // conversions go here
val res = ls.flatMap{
case x: String => mappy.get(x)
} match {
case Seq(y) => y
case Nil => "SOMETHING_ELSE" // the `else` case goes here
case _ => new Exception("could be more than one thing") // handle this however you want
}
我相信這是做的最Scalaesque方式。案例與結果之間的關係在Map
中簡明扼要,您可以根據需要選擇處理多個結果。你說過
名單短(最多4個或5個項目),並只能包含尋求一個值
但有些可能需要處理這種可能性。如果你真的不關心多個匹配,你可以做
val res = ls.flatMap(mappy.get).headOption.getOrElse("SOMETHING_ELSE")
在這兩種情況下,它遍歷只有一次的名單。請享用!
UPDATE:該列表很短(最多4或5個項目),並且只能包含其中一個搜索值。列表實際上代表了樹狀結構中的路徑。我想確定哪個子樹是路徑地址。所以我正在尋找路徑中的特定節點,如果發現我返回該子樹的標識符。問題是我正在尋找的節點可能在樹中的不同層次上,所以我不知道它是否會成爲路徑(列表)中的第1,第2或第4個元素。 – wajda