【问题标题】:takeRightWhile() method in scalascala 中的 takeRightWhile() 方法
【发布时间】:2016-10-12 04:55:17
【问题描述】:

我可能遗漏了一些东西,但最近我遇到了一项任务,即根据某些条件获取最后一个符号。例如我有一个字符串:"this_is_separated_values_5"。现在我想将5 提取为Int

注意:以_ 分隔的部分数量未定义。

如果我在字符串上有一个方法takeRightWhile(f: Char => Boolean),那将是微不足道的:takeRightWhile(ch => ch != '_')。而且它会很有效:一个简单的实现实际上会涉及找到_ 的最后一个索引并获取一个子字符串,而使用这种方法将节省第一步并提供更好的平均时间复杂度。

更新:伙计们,str.reverse.takeWhile(_!='_').reverse 的所有变体都非常低效,因为您实际上使用了额外的 O(n) 空间。如果你想有效地实现方法takeRightWhile,你可以从右边开始迭代,在字符串生成器中累积结果,然后返回结果。我问的是这种方法,而不是问题本身已经描述和拒绝的实现。

问题: scala 标准库中是否存在这种方法?如果没有,是否有标准库中的方法组合以达到相同的最少行数?

提前致谢。

【问题讨论】:

    标签: string scala


    【解决方案1】:

    可能的解决方案:

    str.reverse.takeWhile(_!='_').reverse
    

    更新

    您可以使用 foldRight 从右到左使用以下表达式:

    str.toList.foldRight(List.empty[Char]) {
      case (item, acc) => item::acc
    }
    

    这里需要检查条件,满足条件后停止添加项目。为此,您可以将标志传递给累积值:

    val (_, list) = str.toList.foldRight((false, List.empty[Char])) {
      case (item, (false, list)) if item!='_' => (false, item::list)
      case (_,    (_,     list))              => (true, list)
    }
    val res = list.mkString.toInt
    

    这个解决方案比双反转解决方案效率更低:

    1. foldRight 的实现使用 List reverse 和 foldLeft 的组合

    2. 你不能中断 foldRight 的执行,所以你需要标志来跳过条件满足后的所有项目

    【讨论】:

    • 感谢您的回答。请查看问题的更新。
    • 非常感谢您的更新。至少它不会创建新字符串,因此它比以前的解决方案更有效。也许将其简化为尾递归和 StringBuilder 以提高时间复杂度(换句话说,终止 foldRight)?
    【解决方案2】:

    我会选择这个:

    val s = "string_with_following_number_42"
    s.split("_").reverse.head
    // res:String = 42
    

    这是一个幼稚的尝试,绝不是优化的。它所做的是将String 拆分为ArrayStrings,反转它并获取第一个元素。需要注意的是,由于是在拆分之后反转,所以字符的顺序是正确的。

    【讨论】:

    • 感谢您的回答。然而 split 甚至比 .reverse.take.reverse 方法更糟糕
    • 这是为什么呢?正如我所想,split 和 reverse 都需要遍历整个 String,这将是 O(n)。
    • 从时间复杂度来看你是对的,但是拆分和反向都会导致额外的 n 空间占用
    【解决方案3】:

    我不太确定您面临的问题。我的理解是你想要一个格式为xxx_xxx_xx_...._xxx_123的字符串,并且你想把最后的部分提取为Int

    import scala.util.Try
    
    val yourStr = "xxx_xxx_xxx_xx...x_xxxxx_123"
    
    val yourInt = yourStr.split('_').last.toInt
    
    // But remember that the above is unsafe so you may want to take it as Option
    
    val yourIntOpt = Try(yourStr.split('_').last.toInt).toOption
    

    或者...假设您的要求是收集一个right-suffix,直到一些布尔条件仍然存在true

    import scala.util.Try
    
    val yourStr = "xxx_xxx_xxx_xx...x_xxxxx_123"
    
    val rightSuffix =  yourStr.reverse.takeWhile(c => c != '_').reverse
    
    val yourInt = rightSuffix.toInt
    
    // but above is unsafe so
    val yourIntOpt = Try(righSuffix.toInt).toOption
    

    如果您的要求与此不同,请发表评论。

    【讨论】:

    • 感谢您的回答。然而 split 甚至比 .reverse.take.reverse 方法更糟糕。感谢您指出尝试。
    • 如果您的要求是拆分某些特定字符,实际上拆分方法会更好。但是,如果您需要根据其他布尔条件收集后缀,那么您需要类似reverse.take.reverse
    【解决方案4】:

    您可以使用 StringBuilder 和 lastIndexWhere。

    val str = "this_is_separated_values_5"
    val sb = new StringBuilder(str)
    val lastIdx = sb.lastIndexWhere(ch => ch != '_')
    val lastCh = str.charAt(lastIdx)
    

    【讨论】:

    • 你测试过这个吗?因为如果在最后一个 _ 之后有多个字符,它就不起作用 - OP 显然想要字符串的其余部分,而不是最后一个字符,或者它只是获取最后一个字符的问题!。也不需要 StringBuilder,lastIndexWhere 在 String 上工作。
    • @TheArchetypalPaul 我误解了这个问题。我首先在StringBuilder 中找到了lastIndexWhere,并没有检查StringOps。感谢您指出它们。
    猜你喜欢
    • 1970-01-01
    • 2011-02-18
    • 2014-06-18
    • 1970-01-01
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    • 2015-10-31
    • 2012-01-08
    相关资源
    最近更新 更多