对集合进行抽象
最近,我为 Anys 的笛卡尔积编写了一个迭代器,并从 List of List 开始,但我认识到,我可以轻松切换到更抽象的特征 Seq。
我知道,你喜欢看代码。 :)
class Cartesian (val ll: Seq[Seq[_]]) extends Iterator [Seq[_]] {
def combicount: Int = (1 /: ll) (_ * _.length)
val last = combicount
var iter = 0
override def hasNext (): Boolean = iter < last
override def next (): Seq[_] = {
val res = combination (ll, iter)
iter += 1
res
}
def combination (xx: Seq [Seq[_]], i: Int): List[_] = xx match {
case Nil => Nil
case x :: xs => x (i % x.length) :: combination (xs, i / x.length)
}
}
以及该类的客户端:
object Main extends Application {
val illi = new Cartesian (List ("abc".toList, "xy".toList, "AB".toList))
// val ivvi = new Cartesian (Vector (Vector (1, 2, 3), Vector (10, 20)))
val issi = new Cartesian (Seq (Seq (1, 2, 3), Seq (10, 20)))
// val iaai = new Cartesian (Array (Array (1, 2, 3), Array (10, 20)))
(0 to 5).foreach (dummy => println (illi.next ()))
// (0 to 5).foreach (dummy => println (issi.next ()))
}
/*
List(a, x, A)
List(b, x, A)
List(c, x, A)
List(a, y, A)
List(b, y, A)
List(c, y, A)
*/
该代码适用于 Seq 和列表(它们是 Seq),但当然不适用于数组或向量,它们不是 Seq 类型,并且没有 cons-method ' ::'。
但这个逻辑也可以用于此类集合。
我可以尝试为 Vector、Array 等编写与 Seq 之间的隐式转换,或者尝试编写自己的类似实现,或者编写一个 Wrapper,它将集合转换为 Seq of Seq,并调用“hasNext” 'next' 表示内部集合,并将结果转换为数组、向量或其他类型。 (我试图实现这样的解决方法,但我必须认识到:这并不那么容易。对于现实世界的问题,我可能会独立重写迭代器。)
但是,如果我必须处理,整个事情就会有点失控列表数组或数组列表以及其他混合情况。
以最广泛、可能的方式编写算法的最优雅的方式是什么?
Recently, I wrote an iterator for a cartesian product of Anys, and started with a List of List, but recognized, that I can easily switch to the more abstract trait Seq.
I know, you like to see the code. :)
class Cartesian (val ll: Seq[Seq[_]]) extends Iterator [Seq[_]] {
def combicount: Int = (1 /: ll) (_ * _.length)
val last = combicount
var iter = 0
override def hasNext (): Boolean = iter < last
override def next (): Seq[_] = {
val res = combination (ll, iter)
iter += 1
res
}
def combination (xx: Seq [Seq[_]], i: Int): List[_] = xx match {
case Nil => Nil
case x :: xs => x (i % x.length) :: combination (xs, i / x.length)
}
}
And a client of that class:
object Main extends Application {
val illi = new Cartesian (List ("abc".toList, "xy".toList, "AB".toList))
// val ivvi = new Cartesian (Vector (Vector (1, 2, 3), Vector (10, 20)))
val issi = new Cartesian (Seq (Seq (1, 2, 3), Seq (10, 20)))
// val iaai = new Cartesian (Array (Array (1, 2, 3), Array (10, 20)))
(0 to 5).foreach (dummy => println (illi.next ()))
// (0 to 5).foreach (dummy => println (issi.next ()))
}
/*
List(a, x, A)
List(b, x, A)
List(c, x, A)
List(a, y, A)
List(b, y, A)
List(c, y, A)
*/
The code works well for Seq and Lists (which are Seqs), but of course not for Arrays or Vector, which aren't of type Seq, and don't have a cons-method '::'.
But the logic could be used for such collections too.
I could try to write an implicit conversion to and from Seq for Vector, Array, and such, or try to write an own, similar implementation, or write an Wrapper, which transforms the collection to a Seq of Seq, and calls 'hasNext' and 'next' for the inner collection, and converts the result to an Array, Vector or whatever. (I tried to implement such workarounds, but I have to recognize: it's not that easy. For a real world problem I would probably rewrite the Iterator independently.)
However, the whole thing get's a bit out of control if I have to deal with Arrays of Lists or Lists of Arrays and other mixed cases.
What would be the most elegant way to write the algorithm in the broadest, possible way?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
有两种解决方案。第一个是不要求容器是某个通用超类的子类,而是可以转换为一个(通过使用隐式函数参数)。如果容器已经是所需类型的子类,则有一个预定义的身份转换仅返回它。
这种工作方式:
由于错误 https://issues,我需要显式传递类型。 scala-lang.org/browse/SI-3343
需要注意的一件事是,这比使用存在类型更好,因为在迭代器上调用 next 返回正确的类型,而不是 Seq[Any]。
这里有几个缺点:
现在提供替代解决方案。我们注意到,我们实际上并不关心集合的类型,只关心它们的功能:
foldLeft
、get(i: Int)
(获取 head/tail )length
、get(i: Int)
和一个 Builder所以我们可以对这些进行编码:(
严格来说,HasFold 不是必需的,因为它的实现是根据length 和 get,但我在这里添加了它,所以算法将翻译得更干净)
现在的算法是:
并使用:
Scalaz 可能已经为您预定义了所有这些,不幸的是,我不太了解。
(我再次需要传递类型,因为推理不能推断出正确的类型)
好处是该算法现在完全通用,并且不需要从 Array 到 WrappedArray 的隐式转换才能使其工作
练习:定义对于元组;-)
There are two solutions. The first is to not require the containers to be a subclass of some generic super class, but to be convertible to one (by using implicit function arguments). If the container is already a subclass of the required type, there's a predefined identity conversion which only returns it.
This sort of works:
I needed to explicitly pass the types because of bug https://issues.scala-lang.org/browse/SI-3343
One thing to note is that this is better than using existential types, because calling next on the iterator returns the right type, and not Seq[Any].
There are several drawbacks here:
Now for the alternative solution. We note that we don't actually care about the types of collections, just their capabilities:
foldLeft
,get(i: Int)
(to get head/tail)length
,get(i: Int)
and a BuilderSo we can encode these:
(strictly speaking, HasFold is not required since its implementation is in terms of length and get, but i added it here so the algorithm will translate more cleanly)
now the algorithm is:
And use:
Scalaz probably has all of this predefined for you, unfortunately, I don't know it well.
(again I need to pass the types because inference doesn't infer the right kind)
The benefit is that the algorithm is now completely generic and that there is no need for implicit conversions from Array to WrappedArray in order for it to work
Excercise: define for tuples ;-)