Scala 将多个元组中的 ID 减少为一个 ID
Posted
技术标签:
【中文标题】Scala 将多个元组中的 ID 减少为一个 ID【英文标题】:Scala reduce IDs from multiple tuples into one single ID 【发布时间】:2021-11-01 17:17:28 【问题描述】:我有一个向量vecTest1
,里面有如下的元组,每个元组有 6 个元素,第一个元素是一个键。我想通过一种算法来决定一个 BAB。
这就是我的做法
var strBAB:String="BAB1"
var intNumbBAB=vecTest1(0)._6
for (a<-1 to vecTest1.length-1)
intNumbBAB=intNumbBAB+vecTest1(a)._6
val douAAE = (vecTest1(a-1)._1 - vecTest1(a-1)._2) / vecTest1(a-1)._1
val douAAE1 = (vecTest1(a-1)._1 - vecTest1(a-1)._2 + vecTest1(a-1)._3) / vecTest1(a-1)._1
val douAAE2 = (vecTest1(a)._1 - vecTest1(a)._2 - vecTest1(a-1)._3) / vecTest1(a)._1
if(douAAE1<=.04 && douAAE2>douAAE)
if (vecTest1(a)._6>intNumbBAB) strBAB=vecTest1(a)._1
else strBAB=vecTest1(a-1)._1
有没有更好的方法来做到这一点?我是 Scala 的新手,但这似乎正在减少。那么有没有可能以一种更简洁的方式使用 reduce 呢?
【问题讨论】:
看看zipWithIndex
和foldLeft
另外sliding(2)
可能会有所帮助
我想知道如果你使用多维数组,你的代码会不会更干净。
【参考方案1】:
您似乎想通过依次处理元素的序列来识别特定的元组。任何一种这样的循环都可以转换为某种形式的递归,这是 Scala 作为一种函数式编程语言的惯用方法。
这是一个编写为函数的版本,它返回所寻找的键值。
请注意,我使用类型 (BAB
) 来更简洁地表示元组。我还概括了该方法,使其适用于任何Seq
值(Vector
是IndexedSeq
的一种类型,它是Seq
的一种类型);但在这种情况下,实际上不需要索引值。最后请注意,此实现中没有var
s。
我不得不更改您的代码,因为不允许对字符串进行否定 (-
) 和除法 (/
) 操作,因此我已将对 _1
(String
值)的引用替换为 @ 987654331@(Double
值),将_2
替换为_3
,并将_3
替换为_4
。如果这不是您想要的,我深表歉意,但您的代码无法编译,而且我不知道代码中计算背后的目的。
import scala.annotation.tailrec
type BAB = (String, Double, Double, Double, Double, Int)
def findBAB(babs: Seq[BAB]): String =
// Tail-recursive helper method to find the best BAB.
//
// "best" identifies the key of the best BAB found so far; "num" is
// whatever quantity intNumbBAB represents in your original code; "last"
// is the last BAB considered; and "rem" is the remainder of BAB's to be
// examined, which may be empty.
@tailrec
def find(best: String, num: Int, last: BAB, rem: Seq[BAB]): String =
// If the remainder is empty, then return the best key. We're done.
if(rem.isEmpty) best
// Otherwise, perform another iteration.
else
// Get head tuple of remainder, as the current BAB.
val cur = rem.head
// Calculations.
val newNum = num + cur._6
val douAAE = (last._2 - last._3) / last._2
val douAAE1 = (last._2 - last._3 + last._4) / last._2
val douAAE2 = (cur._2 - cur._3 - last._4) / cur._2
// Do we have a new best BAB key?
val newBest = if(douAAE1 <= 0.04 && douAAE2 > douAAE)
if (cur._6 > newNum) cur._1
else last._1
else best
// Perform the next iteration.
find(newBest, newNum, cur, rem.tail)
// Start the ball rolling by initializing the search.
find(babs.head._1, babs.head._6, babs.head, babs.tail)
上面是一个相当手动的方法,它演示了递归,但可以说它并没有比你目前拥有的改进。
这是另一个使用foldLeft
高阶函数的版本,它更简洁但也可能更不直观:
type BAB = (String, Double, Double, Double, Double, Int)
def findBAB(babs: Seq[BAB]): String =
// Perform a foldLeft on the tail of the sequence.
//
// The "zero" value is a tuple of the key of the first BAB, the "num"
// value, plus the first BAB itself
val result = babs.tail.foldLeft((babs.head._1, babs.head._6, babs.head))
// Break open the tuple arguments.
case ((best, num, last), cur) =>
// Calculations.
val newNum = num + cur._6
val douAAE = (last._2 - last._3) / last._2
val douAAE1 = (last._2 - last._3 + last._4) / last._2
val douAAE2 = (cur._2 - cur._3 - last._4) / cur._2
// Do we have a new best BAB key?
val newBest = if(douAAE1 <= 0.04 && douAAE2 > douAAE)
if (cur._6 > newNum) cur._1
else last._1
else best
// Return a tuple with the values for the next iteration.
(newBest, newNum, cur)
// Return the first value of the tuple result (the BAB key).
result._1
reduce
操作是不可能的,因为需要的信息不仅仅是序列中的值(最佳 BAB 的键和神秘的“num”值)。
我还会考虑使用case class
代替元组,以便可以命名每个BAB 中的值,而不是通过数字引用。例如:
final case class BAB(key: String, v1: Double, v2: Double, v3: Double,
v4: Double, num: Int)
案例类以很少的开销添加了功能。
【讨论】:
感谢您的意见。神秘的“数字”?用来决定最佳 BAB 的一切都来自这个 Vector。你的意思是我的代码中的 intNumbBAB?以上是关于Scala 将多个元组中的 ID 减少为一个 ID的主要内容,如果未能解决你的问题,请参考以下文章