Scala - 如何定义地图,其中值取决于键?
Scala - How to define map, where value depends on key?
有没有办法定义一个 Map,其中 Map 值取决于它的键,比如
Map(key -> f(key), key2 -> f(key2), ...).
假设您的键在这样的列表中,并且您想要将其转换为以正方形作为值的映射。
scala> val keyList = ( 1 to 10 ).toList
keyList: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> val doSquare = ( x: Int ) => x * x
doSquare: Int => Int = <function1>
// Convert it to the list of tuples - ( key, doSquare( key ) )
scala> val tupleList = keyList.map( key => ( key, doSquare( key ) ) )
tuple: List[(Int, Int)] = List((1,1), (2,4), (3,9), (4,16), (5,25), (6,36), (7,49), (8,64), (9,81), (10,100))
val keyMap = tuple.toMap
keyMap: scala.collection.immutable.Map[Int,Int] = Map(5 -> 25, 10 -> 100, 1 -> 1, 6 -> 36, 9 -> 81, 2 -> 4, 7 -> 49, 3 -> 9, 8 -> 64, 4 -> 16)
或者一行完成
( 1 to 10 ).toList.map( x => ( x, x * x ) ).toMap
或者...如果你只有几个键...那么你可以写具体的代码
Map( 1 -> doSquare( 1 ), 2 -> doSquare( 2 ) )
您可以使用以下方法制作无限正方形地图:
val mySquareMap = Map.empty[Int, Int].withDefault(d => d * d)
这张地图仍然有 +
、get
、iterator
和其他无法按预期工作的方法,但是如果您需要 returns 方块,这可行。
当然,直接使用会更高效,也可能更清晰:
val mySquare = (d:Int) => d * d
作为一个函数。但是,如果您需要使用某些需要该类型的 API,上述 Map
可能会有用。
要对此有一个更完整的解决方案,您最好构建自己的 class,扩展 Map[Int, Int]
,覆盖 get
到 return其参数的平方。
你看错了...
一个Map[K,V]
也是Partialfunction[K,V]
的一个实例。因此,将您使用 Map
类型(val、方法参数等)的任何地方都更改为 PartialFunction
.
然后您可以直接使用 f
,或者在键和值之间没有简单代数关系的情况下提供 Map[K,V]
作为实例。
例如
def methodUsingMapping(x: PartialFunction[Int,Boolean]) = ...
//then
val myMap = Map(1->true, 2->true, 3->false)
methodUsingMapping(myMap)
//or
val isEven = PartialFunction(n: Int => n % 2 == 0)
methodUsingMapping(isEven)
//or
//note: case statements in a block is the smart way
// to define a partial function
// In this version, the result isn't even defined for odd numbers
val isEven: PartialFunction[Int,Boolean] = {
case n: Int if n % 2 == 0 => true
}
methodUsingMapping(isEven)
您可能还想考虑使用 (K) => Option[V]
,在这种情况下,您可以通过 lift
方法提供该类型的实例,该映射继承自 PartialFunction
例如
def methodUsingMapping(x: (Int)=>Option[Boolean]) = ...
//then
val myMap = Map(1->true, 2->true, 3->false)
methodUsingMapping(myMap.lift)
//or
def isEven(n: Int) = Some(n % 2 == 0)
methodUsingMapping(isEven)
//or
def isEven(n: Int) = n % 2 == 0
methodUsingMapping(x => Some(isEven(x)))
因为你只需要定义 4 个方法来实现 Map
特征,你可以自己动手:
trait MapWithRelationship[K, +V] extends Map[K, V] {
self =>
def pred: (K, Any) => Boolean
def underlying: Map[K, V]
def get(key: K): Option[V] = underlying.get(key)
def iterator: Iterator[(K, V)] = underlying.iterator
def + [V1 >: V](kv: (K, V1)): MapWithRelationship[K, V1] = {
val (k, v) = kv
if (pred(k, v)) {
new MapWithRelationship[K, V1] {
val pred = self.pred
val underlying = self.underlying + kv
}
} else {
throw new Exception(s"Key-value pair $kv failed MapWithRelationship predicate")
}
}
def -(key: K): MapWithRelationship[K, V] =
new MapWithRelationship[K, V] {
val pred = self.pred
val underlying = self.underlying - key
}
}
object MapWithRelationship {
def apply[K, V](rule: (K, Any) => Boolean)(pairs: (K, V)*) = {
val empty = new MapWithRelationship[K, V] {
def pred = rule
def underlying = Map.empty[K, V]
}
pairs.foldLeft(empty)(_ + _)
}
}
那么你可以这样使用:
scala> val x = MapWithRelationship[Int, Int]((k, v) => v == k * k)()
x: MapWithRelationship[Int,Int] = Map()
scala> val x2 = x + (1 -> 1)
x2: MapWithRelationship[Int,Int] = Map(1 -> 1)
scala> val x3 = x + (5 -> 25)
x3: MapWithRelationship[Int,Int] = Map(5 -> 25)
scala> val x4 = x + (6 -> "foo")
java.lang.Exception: Key-value pair (6,foo) failed MapWithRelationship predicate
at MapWithRelationship$class.$plus(<console>:21)
at MapWithRelationship$$anon.$plus(<console>:33)
... 32 elided
有没有办法定义一个 Map,其中 Map 值取决于它的键,比如
Map(key -> f(key), key2 -> f(key2), ...).
假设您的键在这样的列表中,并且您想要将其转换为以正方形作为值的映射。
scala> val keyList = ( 1 to 10 ).toList
keyList: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> val doSquare = ( x: Int ) => x * x
doSquare: Int => Int = <function1>
// Convert it to the list of tuples - ( key, doSquare( key ) )
scala> val tupleList = keyList.map( key => ( key, doSquare( key ) ) )
tuple: List[(Int, Int)] = List((1,1), (2,4), (3,9), (4,16), (5,25), (6,36), (7,49), (8,64), (9,81), (10,100))
val keyMap = tuple.toMap
keyMap: scala.collection.immutable.Map[Int,Int] = Map(5 -> 25, 10 -> 100, 1 -> 1, 6 -> 36, 9 -> 81, 2 -> 4, 7 -> 49, 3 -> 9, 8 -> 64, 4 -> 16)
或者一行完成
( 1 to 10 ).toList.map( x => ( x, x * x ) ).toMap
或者...如果你只有几个键...那么你可以写具体的代码
Map( 1 -> doSquare( 1 ), 2 -> doSquare( 2 ) )
您可以使用以下方法制作无限正方形地图:
val mySquareMap = Map.empty[Int, Int].withDefault(d => d * d)
这张地图仍然有 +
、get
、iterator
和其他无法按预期工作的方法,但是如果您需要 returns 方块,这可行。
当然,直接使用会更高效,也可能更清晰:
val mySquare = (d:Int) => d * d
作为一个函数。但是,如果您需要使用某些需要该类型的 API,上述 Map
可能会有用。
要对此有一个更完整的解决方案,您最好构建自己的 class,扩展 Map[Int, Int]
,覆盖 get
到 return其参数的平方。
你看错了...
一个Map[K,V]
也是Partialfunction[K,V]
的一个实例。因此,将您使用 Map
类型(val、方法参数等)的任何地方都更改为 PartialFunction
.
然后您可以直接使用 f
,或者在键和值之间没有简单代数关系的情况下提供 Map[K,V]
作为实例。
例如
def methodUsingMapping(x: PartialFunction[Int,Boolean]) = ...
//then
val myMap = Map(1->true, 2->true, 3->false)
methodUsingMapping(myMap)
//or
val isEven = PartialFunction(n: Int => n % 2 == 0)
methodUsingMapping(isEven)
//or
//note: case statements in a block is the smart way
// to define a partial function
// In this version, the result isn't even defined for odd numbers
val isEven: PartialFunction[Int,Boolean] = {
case n: Int if n % 2 == 0 => true
}
methodUsingMapping(isEven)
您可能还想考虑使用 (K) => Option[V]
,在这种情况下,您可以通过 lift
方法提供该类型的实例,该映射继承自 PartialFunction
例如
def methodUsingMapping(x: (Int)=>Option[Boolean]) = ...
//then
val myMap = Map(1->true, 2->true, 3->false)
methodUsingMapping(myMap.lift)
//or
def isEven(n: Int) = Some(n % 2 == 0)
methodUsingMapping(isEven)
//or
def isEven(n: Int) = n % 2 == 0
methodUsingMapping(x => Some(isEven(x)))
因为你只需要定义 4 个方法来实现 Map
特征,你可以自己动手:
trait MapWithRelationship[K, +V] extends Map[K, V] {
self =>
def pred: (K, Any) => Boolean
def underlying: Map[K, V]
def get(key: K): Option[V] = underlying.get(key)
def iterator: Iterator[(K, V)] = underlying.iterator
def + [V1 >: V](kv: (K, V1)): MapWithRelationship[K, V1] = {
val (k, v) = kv
if (pred(k, v)) {
new MapWithRelationship[K, V1] {
val pred = self.pred
val underlying = self.underlying + kv
}
} else {
throw new Exception(s"Key-value pair $kv failed MapWithRelationship predicate")
}
}
def -(key: K): MapWithRelationship[K, V] =
new MapWithRelationship[K, V] {
val pred = self.pred
val underlying = self.underlying - key
}
}
object MapWithRelationship {
def apply[K, V](rule: (K, Any) => Boolean)(pairs: (K, V)*) = {
val empty = new MapWithRelationship[K, V] {
def pred = rule
def underlying = Map.empty[K, V]
}
pairs.foldLeft(empty)(_ + _)
}
}
那么你可以这样使用:
scala> val x = MapWithRelationship[Int, Int]((k, v) => v == k * k)()
x: MapWithRelationship[Int,Int] = Map()
scala> val x2 = x + (1 -> 1)
x2: MapWithRelationship[Int,Int] = Map(1 -> 1)
scala> val x3 = x + (5 -> 25)
x3: MapWithRelationship[Int,Int] = Map(5 -> 25)
scala> val x4 = x + (6 -> "foo")
java.lang.Exception: Key-value pair (6,foo) failed MapWithRelationship predicate
at MapWithRelationship$class.$plus(<console>:21)
at MapWithRelationship$$anon.$plus(<console>:33)
... 32 elided