【问题标题】:How to return a yielded value from a for-loop in scala?如何从scala中的for循环返回产生的值?
【发布时间】:2020-10-26 06:27:27
【问题描述】:

在我的项目中,我们正在从 Java 迁移到 Scala。我必须使用 Java 中的一个函数,该函数在 for 循环中使用 continue,并根据 for 循环内的 if 条件返回一个值,如下所示。

private TSourceToken getBeforeToken(TSourceToken token) {
  TSourceTokenList tokens = token.container;
  int index = token.posinlist;     
  for ( int i = index - 1; i >= 0; i-- ) {
    TSourceToken currentToken = tokens.get( i );
    if ( currentToken.toString( ).trim( ).length( ) == 0 ) {
      continue;
    }
    else {
      return currentToken;
    }
  }
  return token;
}

为了将其转换为 Scala,我使用了 Yield 选项来过滤掉满足 if 表达式的 else 条件的值,如下所示。

def getBeforeToken(token: TSourceToken): TSourceToken = {
  val tokens = token.container
  val index = token.posinlist

  for { i <- index -1 to 0 by -1
        currentToken = tokens.get(i)
        if(currentToken.toString.trim.length != 0)
        } yield currentToken

}

我无法弄清楚如何返回 for 循环产生的值。错误信息是

type mismatch;
Found   : IndexedSeq[TSourceToken]
Required: TSourceToken

我知道yield 收集了 for 循环中的所有值及其内部条件,并导致了一个集合:IndexedSeq & 因此错误消息。我无法想出用 Java 代码编写的逻辑。 谁能告诉我如何在不使用yield 的情况下在Scala 中构建代码并在满足else 条件后打破for循环?

【问题讨论】:

  • 哦好吧..现在明白了。它是一个 java Iterator.
  • 为什么不把它也转换成 Scala 类型呢?

标签: scala


【解决方案1】:

在 scala 中没有像 java 中那样常见的 for-loopsfor-yieldfor-loop 不同。这只是flatMapmapwithFilter 函数组合的语法糖。同样在 scala 中最好不要使用 return 关键字。基于有返回值和return字的表达式的语言会破坏逻辑,这是其他开发人员无法预料的。

在您的情况下,最好使用find 通过谓词查找某些集合中的某些特定元素:

def getBeforeToken(token: TSourceToken): TSourceToken = {
  val tokens = token.container
  val index = token.posinlist

  (index - 1 to 0 by -1)
    .find(i => tokens.get(i).toString.trim.length != 0)
    .fold(token)(i => tokens.get(i))
}

【讨论】:

  • 那么在这种情况下,find 是否会为第一个值打破循环,满足条件?我的理解正确吗?
  • @Metadata 是的,它会找到第一个满足的令牌或使用传递给getOrElse的默认值
  • 我觉得这可能会更容易扭转它,比如token.container.view.take(index).reverse.find(_.toString.trim.nonEmpty).getOrElse(token)
【解决方案2】:

用于理解的工具不是解决这个问题的正确工具:它是用于映射,而不是用于查找。

对于直接将您的 java 代码重写为 scala 的直接重写,请使用 while 循环:

def getBeforeToken(token: TSourceToken): TSourceToken = {
  val tokens = token.container
  var i = index - 1
  while (i >= 0) {
    val currentToken = tokens.get(i)
    if (currentToken.toString.trim.length != 0) return currentToken
    i -= 1
  }
  token
}

如果token.container 是一些集合,您可能会在没有显式迭代的情况下编写它:

def getBeforeToken(token: TSoruceToken): TSourceToken =
  token.container.reverseIterator.collectFirst {
    case t if t.toString.trim.length != 0 => t
  }.getOrElse(token)

在迁移过程中,最好坚持第一个,尽可能快地完成几乎机械的翻译,然后再尝试提高 scala 代码质量。

【讨论】:

    【解决方案3】:

    您可以阅读很多有关 Scala 产量的信息。这里有一些不错的文章:

    但我认为这是在 Scala 中处理此问题的错误方法。 Scala 是一种函数式编程语言,您可以使用它的功能。

    假设TSourceToken 是具有两个成员的案例类:

    case class TSourceToken(container: Seq[String], posinlist: Int)
    

    您要从最后一个 posinlist 中选择最后一个不为空的标记的位置。有很多方法可以实现这一目标。我正在考虑的一种方法是:

    val tokens = TSourceToken(Seq("", "   ", "  \n   ", "real token", "\t\t", "   ", "  \n\n  ", "token to ignore"), 5)
    val token = tokens.container.take(tokens.posinlist).filter(_.trim.nonEmpty).last
    

    令牌将有real token,如您所料。代码运行可以在Scastie找到。

    【讨论】:

      猜你喜欢
      • 2017-02-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-05
      • 2013-01-18
      • 2011-07-05
      • 1970-01-01
      • 2021-01-05
      相关资源
      最近更新 更多