scala zip列表到元组
Posted
技术标签:
【中文标题】scala zip列表到元组【英文标题】:scala zip list to tuple 【发布时间】:2012-07-06 23:51:26 【问题描述】:使用 JodaTime,尝试将 List[LocalDate] 转换为 Tuple2[JodaTime, JodaTime],这样我就可以像这样进行多重分配:
val(expire, now) =
List(row.expireDate, new JodaDate) zip (_.toDateTimeAtStartOfDay.getMillis)
当然不能编译。是否有类似简洁的方法来完成上述操作?我知道我可以手动完成:
val(expire, now) =
(row.expireDate.toDateTimeAtStartOfDay.getMillis,
new JodaDate().toDateTimeAtStartOfDay.getMillis)
但这有点难看
【问题讨论】:
IMO 是一个相当夸张的问题。为什么要引入所有用于创建List
、函数字面量、迭代和模式匹配的冗余动态计算周期?我的意思是这一切的真正目的是什么?混淆一个简单的代码?那么这是一个糟糕的编码风格的好例子。问题再简单不过了:您只需要分配两个变量,因此分配它们:val expire = row.expireDate.toDateTimeAtStartOfDay.getMillis; val now = new JodaDate().toDateTimeAtStartOfDay.getMillis
- 在此之前无需建造房屋并拆除它。
@NikitaVolkov, *cough*
@missingfaktor 这些是完全不同的东西。不过喜欢你的讽刺)
@NikitaVolkov 可以肯定地说,尽管服务器上承受着巨大的负载,但接受的答案不会将我的应用程序拖入爬行状态,咳嗽 ;-)
【参考方案1】:
val Seq(expire, now) =
Seq(row.expireDate, new JodaDate).map(_.toDateTimeAtStartOfDay.getMillis)
【讨论】:
都是模式匹配和提取器。你也可以val expire :: now :: Nil = List(row.expireDate, new JodaDate).map(_.toDateTimeAtStartOfDay.getMillis)
.
我知道 val a :: b 赋值,但一直使用 val(a,b) 以获得更好的语法。无论如何,显然有很多方法可以在 Scala 中实现相同的目标【参考方案2】:
你想要的(假设你不想转换到Seq
路线)是Scalaz's Bifunctor
instance 用于元组(不在标准库中)。有了它,您可以编写以下内容:
scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._
scala> val cap = (_: String).toUpperCase
cap: String => java.lang.String = <function1>
scala> val removeLs = (_: String).replaceAll("l", "")
removeLs: String => java.lang.String = <function1>
scala> cap <-: ("hello", "world") :-> removeLs
res0: (java.lang.String, java.lang.String) = (HELLO,word)
或者,在你的情况下:
val f = (_: JodaDate).toDateTimeAtStartOfDay.getMillis
val (expire, now) = f <-: (row.expireDate, new JodaDate) :-> f
【讨论】:
啊啊啊啊啊,象形文字语法汤,不用了,谢谢。还没有为 Scalaz 做好准备,还在学习 Scala 本身——我肯定会深入研究,但不是现在;-) 实际上并没有那么复杂,在元组上“映射”非常方便——这是我实际使用的少数 Scalaz 之一。 @virtualeyes,f <-: (a, b) :-> g = (f(a), g(b))
.
Scalaz 网关药物似乎是 Validation,这很可能是我的切入点。我在这里 +1 了,是的,再次查看您的示例,我看到这种方法很有用。【参考方案3】:
val Seq(a, b) =
Seq("a", "b").map(_.toUpperCase)
println("a, b = %s, %s".format(a, b)) // a, b = A, B
【讨论】:
该死,missingfaktor 领先我一分钟。 无论如何我都加了你,不用担心 ;-)【参考方案4】:如果你想保持使用元组的类型安全(记住,当取消应用Seq
时,编译器不会检查长度),你可以编写一个包装器来添加一个标准库中不可用的函数,这将允许您映射一个元组。
在使用单个函数映射两个元素的情况下,因为Tuple2[A, B]
有两个类型参数,所以实现这项工作的关键是需要证明A
和B
是相同类型的证据。为此,需要B =:= A
类型的隐式参数;如果类型确实相等,编译器将提供B => A
类型的函数。
class Tuple2Wrapper[A, B](t: (A, B))
def bimap[C, D](f: A => C, g: B => D): (C, D) = (f(t._1), g(t._2))
def <-:->[C](f: A => C)(implicit ev: B =:= A): (C, C) = bimap(f, f compose ev)
implicit def tuple2Tuple2Wrapper[A, B](t: (A, B)) = new Tuple2Wrapper(t)
scala> (1, 1) <-:-> (_ + 1)
res1: (Int, Int) = (2,2)
如果根据 Scalaz 的 Bifunctor
特征实现,这可以以更通用和有用的方式完成(适用于更多类型,而不仅仅是 Tuple2
)。
【讨论】:
包装器有什么理由不应该只采用 (A,A) 元组?为什么要添加另一个参数然后检查它是否相同? @Kaito 你是对的,包装器可以只为(A, A)
编写。我最初将它作为包装器上的专用方法与其他 [A, B]
-parameterised 方法一起编写,并且在删除这些其他方法后没有考虑简化示例。以上是关于scala zip列表到元组的主要内容,如果未能解决你的问题,请参考以下文章