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 &lt;-: (a, b) :-&gt; 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] 有两个类型参数,所以实现这项工作的关键是需要证明AB 是相同类型的证据。为此,需要B =:= A 类型的隐式参数;如果类型确实相等,编译器将提供B =&gt; 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列表到元组的主要内容,如果未能解决你的问题,请参考以下文章

Python列表操作:将前任和后继组合到元组中

如何将列表合并到元组列表中?

Haskell:如何附加到元组列表列表?

将列表作为单个元素插入到元组中

附加到元组中定义的列表 - 这是一个错误吗? [复制]

在 Python 中查找输入字符串到元组列表的所有可能匹配项(以任何顺序/顺序)