scala 语法:连接符"~"和 at符“@”

本文深入解析Scala中的连接符~和at符@的用法。通过具体示例代码展示了如何使用这些符号进行表达式解析及模式匹配操作。

一、scala 连接符~解析

import scala.util.parsing.combinator._

object ArithExpr extends JavaTokenParsers {
  

  def expr: Parser[Any] = {

   
     log(term)("加减法表达式  term") ~ rep("+" ~ log(term)("加法 term")  | "-"~ log(term)("减法  term") )
  }
  def term: Parser[Any] = log(factor)("乘除法表达式   factor")  ~ rep("*" ~ log(factor)("乘法 factor") | "/" ~ log(factor)("除法  factor"))
  def factor: Parser[Any] = (wholeNumber | decimalNumber | floatingPointNumber) | "(" ~ log(expr)("factor expr") ~ ")"

  def main(args: Array[String]) {

  
    println(parseAll(expr, "(1*2)* 5"))
  }
}



运行结果 如下 


"C:\Program Files\Java\jdk1.8.0_65\bin\java" -Didea.launcher.port=7532 "-Didea.launcher.bin.path=C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 15.0.2\bin" -......" com.intellij.rt.execution.application.AppMain ArithExpr
trying 加减法表达式  term at scala.util.parsing.input.CharSequenceReader@1fd3711
trying 乘除法表达式   factor at scala.util.parsing.input.CharSequenceReader@1fd3711
trying factor expr at scala.util.parsing.input.CharSequenceReader@f9b8bc
trying 加减法表达式  term at scala.util.parsing.input.CharSequenceReader@f9b8bc
trying 乘除法表达式   factor at scala.util.parsing.input.CharSequenceReader@f9b8bc
乘除法表达式   factor --> [1.3] parsed: 1
trying 乘法 factor at scala.util.parsing.input.CharSequenceReader@146e1f4
乘法 factor --> [1.5] parsed: 2
加减法表达式  term --> [1.5] parsed: (1~List((*~2)))
factor expr --> [1.5] parsed: ((1~List((*~2)))~List())
乘除法表达式   factor --> [1.6] parsed: (((~((1~List((*~2)))~List()))~))
trying 乘法 factor at scala.util.parsing.input.CharSequenceReader@144fc66
乘法 factor --> [1.9] parsed: 5
加减法表达式  term --> [1.9] parsed: ((((~((1~List((*~2)))~List()))~))~List((*~5)))
[1.9] parsed: (((((~((1~List((*~2)))~List()))~))~List((*~5)))~List())
Process finished with exit code 0


二、Scala at符@

scala中 @ 除了是注解 以及 抽取XML的属性 功能以外 , 还有一个功能就是模式匹配中 匹配 类似 some的值



  * Created by admin on 2016/12/28.
  */
object test6 {
  def main(args: Array[String]) {

   val d@(c@Some(a), Some(b)) = (Some(1), Some(2))
    println(d)
    println(c)
    println(a)
    println(b)

    println()
     (Some(1), Some(2)) match {
      case d@(c@Some(a), Some(b)) => println(a, b, c, d)
    }

    println()
    println()
    for (x @ Some(y) <- Seq(None, Some(1))) println(x, y)
    val List(x, xs@_*) = List(1, 2, 3)
    println(x)
    println(xs)
    println()
    val o: Option[Int] = Some(2)
    o match {
      case Some(x) => println(x)
      case None =>
    }
    o match {
      case x@Some(_) => println(x)
      case None =>
    }


  }
}

运行结果 

"C:\Program Files\Java\jdk1.8.0_65\bin\java" -Didea.launcher.port=7537 "-Didea.launcher.bin.path=.......r" com.intellij.rt.execution.application.AppMain test6
(Some(1),Some(2))
Some(1)
1
2


(1,2,Some(1),(Some(1),Some(2)))




(Some(1),1)
1
List(2, 3)


2
Some(2)


Process finished with exit code 0


Caused by: java.lang.IndexOutOfBoundsException: 100 at scala.collection.mutable.ResizableArray$class.apply(ResizableArray.scala:43) ~[scala-library-2.11.8.jar:?] at scala.collection.mutable.ArrayBuffer.apply(ArrayBuffer.scala:48) ~[scala-library-2.11.8.jar:?] at com.bmsoft.operate.VsZConductorOverlimiteventPeriod$$anonfun$3$$anonfun$apply$6$$anonfun$apply$9.apply(VsZConductorOverlimiteventPeriod.scala:389) ~[_55816ee26fbdccb93561c3f306af0e74.jar:?] at com.bmsoft.operate.VsZConductorOverlimiteventPeriod$$anonfun$3$$anonfun$apply$6$$anonfun$apply$9.apply(VsZConductorOverlimiteventPeriod.scala:310) ~[_55816ee26fbdccb93561c3f306af0e74.jar:?] at scala.collection.mutable.ResizableArray$class.foreach(ResizableArray.scala:59) ~[scala-library-2.11.8.jar:?] at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:48) ~[scala-library-2.11.8.jar:?] at com.bmsoft.operate.VsZConductorOverlimiteventPeriod$$anonfun$3$$anonfun$apply$6.apply(VsZConductorOverlimiteventPeriod.scala:310) ~[_55816ee26fbdccb93561c3f306af0e74.jar:?] at com.bmsoft.operate.VsZConductorOverlimiteventPeriod$$anonfun$3$$anonfun$apply$6.apply(VsZConductorOverlimiteventPeriod.scala:266) ~[_55816ee26fbdccb93561c3f306af0e74.jar:?] at scala.collection.Iterator$$anon$12.nextCur(Iterator.scala:434) ~[scala-library-2.11.8.jar:?] at scala.collection.Iterator$$anon$12.hasNext(Iterator.scala:440) ~[scala-library-2.11.8.jar:?] at scala.collection.Iterator$$anon$11.hasNext(Iterator.scala:408) ~[scala-library-2.11.8.jar:?] at org.apache.spark.sql.catalyst.expressions.GeneratedClass$GeneratedIteratorForCodegenStage1.processNext(Unknown Source) ~[?:?] at org.apache.spark.sql.execution.BufferedRowIterator.hasNext(BufferedRowIterator.java:43) ~[spark-sql_2.11-2.3.0-odps0.30.0.jar:?] at org.apache.spark.sql.execution.WholeStageCodegenExec$$anonfun$10$$anon$1.hasNext(WholeStageCodegenExec.scala:614) ~[spark-sql_2.11-2.3.0-odps0.30.0.jar:?] at org.apache.spark.sql.execution.UnsafeExternalRowSorter.sort(UnsafeExternalRowSorter.java:216) ~[spark-catalyst_2.11-2.3.0-odps0.30.0.jar:?] at org.apache.spark.sql.execution.exchange.ShuffleExchangeExec$$anonfun$2.apply(ShuffleExchangeExec.scala:279) ~[spark-sql_2.11-2.3.0-odps0.30.0.jar:?] at org.apache.spark.sql.execution.exchange.ShuffleExchangeExec$$anonfun$2.apply(ShuffleExchangeExec.scala:250) ~[spark-sql_2.11-2.3.0-odps0.30.0.jar:?] at org.apache.spark.rdd.RDD$$anonfun$mapPartitionsInternal$1$$anonfun$apply$25.apply(RDD.scala:830) ~[spark-core_2.11-2.3.0-odps0.30.0.jar:?] at org.apache.spark.rdd.RDD$$anonfun$mapPartitionsInternal$1$$anonfun$apply$25.apply(RDD.scala:830) ~[spark-core_2.11-2.3.0-odps0.30.0.jar:?] at org.apache.spark.rdd.MapPartitionsRDD.compute(MapPartitionsRDD.scala:38) ~[spark-core_2.11-2.3.0-odps0.30.0.jar:?] at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:324) ~[spark-core_2.11-2.3.0-odps0.30.0.jar:?] at org.apache.spark.rdd.RDD.iterator(RDD.scala:288) ~[spark-core_2.11-2.3.0-odps0.30.0.jar:?] at org.apache.spark.rdd.MapPartitionsRDD.compute(MapPartitionsRDD.scala:38) ~[spark-core_2.11-2.3.0-odps0.30.0.jar:?] at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:324) ~[spark-core_2.11-2.3.0-odps0.30.0.jar:?] at org.apache.spark.rdd.RDD.iterator(RDD.scala:288) ~[spark-core_2.11-2.3.0-odps0.30.0.jar:?] at org.apache.spark.scheduler.ShuffleMapTask.runTask(ShuffleMapTask.scala:96) ~[spark-core_2.11-2.3.0-odps0.30.0.jar:?] at org.apache.spark.scheduler.ShuffleMapTask.runTask(ShuffleMapTask.scala:53) ~[spark-core_2.11-2.3.0-odps0.30.0.jar:?] at org.apache.spark.scheduler.Task.run(Task.scala:109) ~[spark-core_2.11-2.3.0-odps0.30.0.jar:?] at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:345) ~[spark-core_2.11-2.3.0-odps0.30.0.jar:?] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) ~[?:1.8.0_65-AliJVM] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) ~[?:1.8.0_65-AliJVM] at java.lang.Thread.run(Thread.java:745) ~[?:1.8.0_65-AliJVM]
最新发布
08-27
<think>我们正在处理一个关于在ScalaSpark中使用ArrayBuffer时遇到的IndexOutOfBoundsException异常的问题。 根据用户提供的引用问题,我们可以推断用户可能在使用Spark时遇到了数组越界异常,特别是在使用ArrayBuffer的场景下。 引用[1]提到了一种情况:在读取CSV时出现ArrayIndexOutOfBoundsException:62或63,这可能与CSV文件的列数不一致有关。 引用[2]提到,当使用insert overwrite table时,目标表的字段数目必须与临时表的字段数目一致,否则会导致数组越界。 但是,用户的问题更具体:在Scala的ArrayBuffer中,由Spark执行引起的IndexOutOfBoundsException。 因此,我们需要分析在Scala中使用ArrayBuffer时,在分布式计算环境下可能出现的数组越界问题。 常见原因: 1. 多线程并发修改:ArrayBuffer不是线程安全的。在Spark的分布式计算中,多个任务(task)可能同时修改同一个ArrayBuffer实例,导致内部状态不一致,从而引发越界。 2. 在算子中使用了共享的可变集合:在Spark的算子(如map、filter等)内部,如果引用了Driver端的一个可变集合(如ArrayBuffer),并且尝试修改它,那么每个Task都会操作这个集合,而Task是并行执行的,这会导致并发问题。 3. 在RDD操作中错误地使用了本地集合:例如,在RDD的transformations中,如果使用了Driver端的ArrayBuffer,并且进行了修改,由于这个ArrayBuffer会被序列化到各个Executor,而每个Executor上的Task都会修改自己的副本,但如果有多个Task在同一个Executor上并行执行,则可能同时修改同一个副本(如果这个ArrayBuffer被多个Task共享),从而引发问题。 解决方案: 1. 避免在算子内部修改共享的集合。在Spark中,算子内部的代码是在Executor上执行的,而Driver端的变量会被序列化后发送到Executor。如果需要在算子中修改集合,应该使用每个Task独立的局部变量。 2. 如果需要在多个Task之间共享状态,应该使用Spark提供的共享变量,如广播变量(Broadcast)或累加器(Accumulator)。但是注意,累加器只能用于累加操作,不能用于收集数据(如往集合里添加元素)。 3. 如果确实需要收集数据,可以使用RDD的collect操作将数据收集到Driver端,然后在Driver端操作ArrayBuffer。但是,collect操作会将所有数据拉到Driver端,因此只适用于小数据量。 4. 使用线程安全的集合:在Scala中,可以使用`scala.collection.mutable.SynchronizedBuffer`,但注意,这已经过时。推荐使用`java.util.concurrent`包中的并发集合,或者使用Scala的`scala.collection.concurrent.TrieMap`等。但是,在分布式环境下,即使是线程安全的集合,在多个节点上修改同一个集合也是不现实的,因为每个节点有自己的JVM,修改的是不同的副本。 因此,在Spark中,更常见的做法是: - 避免在Task中修改共享的集合(尤其是可变集合)。 - 如果需要在Task中收集数据,可以使用RDD的聚合操作(如aggregate、fold等)来合并结果,这样每个Task操作自己的局部集合,然后合并到Driver端。 示例:假设我们有一个RDD,我们想收集满足某些条件的元素到一个集合中。 错误做法: ```scala val sharedBuffer = ArrayBuffer[Int]() rdd.foreach { element => // 多个Task同时修改同一个ArrayBuffer(实际上每个Task得到的是这个buffer的一个副本,但同一个Executor上的多个Task可能同时修改同一个副本,因为Driver端的变量在每个Executor上只有一个副本,被多个Task共享) sharedBuffer += element * 2 } ``` 正确做法: 使用`collect`(如果数据量小): ```scala val collected = rdd.collect().toBuffer ``` 或者使用聚合操作(例如,我们想收集所有元素): ```scala val collected = rdd.collect() // 或者使用aggregate,但这里用collect更直接 ``` 如果数据量大,且需要过滤后收集,可以先过滤再收集: ```scala val filtered = rdd.filter(...).collect() ``` 如果需要在每个Task中收集,然后合并,可以使用`aggregate`: ```scala val zero = ArrayBuffer[Int]() val seqOp = (buf: ArrayBuffer[Int], value: Int) => buf += value val combOp = (buf1: ArrayBuffer[Int], buf2: ArrayBuffer[Int]) => buf1 ++= buf2 val result = rdd.aggregate(zero)(seqOp, combOp) ``` 但是注意,在聚合操作中,我们使用每个Task处理自己的局部ArrayBuffer,然后在最后合并,这样避免了并发修改。 另外,如果用户是在自定义的代码中使用ArrayBuffer(比如在UDF中),那么要确保在每个UDF调用中,ArrayBuffer是局部创建的,而不是共享的。 总结: 在Spark中遇到IndexOutOfBoundsException,特别是在使用ArrayBuffer时,首先要考虑是否在并发环境下错误地共享了可变状态。解决方案是使用局部变量或使用Spark的聚合操作来避免并发修改。 相关问题: 1. 在Spark中,如何安全地收集数据到Driver端? 2. 如何在Spark中实现自定义的聚合操作? 3. Spark中的共享变量(广播变量累加器)如何使用? 4. 在Scala中,有哪些线程安全的集合可以在Spark中使用? 5. 如何调试Spark中的数组越界异常?</think>### ScalaSpark中`IndexOutOfBoundsException`的原因与解决方案 在Scala的`ArrayBuffer`结合Spark执行时出现`java.lang.IndexOutOfBoundsException`,通常是由于**并发访问冲突**或**数据分区不一致**导致的。以下是具体分析解决方案: --- #### 主要原因 1. **并发修改问题(最常见)** - `ArrayBuffer`是**非线程安全**的可变集合。在Spark分布式计算中,多个Executor的Task可能同时修改同一个`ArrayBuffer`实例,导致内部索引混乱。 - 示例场景: ```scala val sharedBuffer = ArrayBuffer[Int]() // Driver端创建 rdd.foreach { x => sharedBuffer += x * 2 // 多个Task并发修改! } ``` 2. **数据分区不均** - Spark操作(如`mapPartitions`)中,若分区内数据量超出预期,访问索引越界: ```scala rdd.mapPartitions { iter => val buffer = iter.toArray // 分区数据转数组 buffer(100) // 若分区数据量<100则越界 } ``` 3. **Schema不匹配(Spark读写场景)** - 写入Hive表时,目标表与DataFrame的字段数量不一致(引用[2]): ```scala df.write.insertInto("table") // 表字段数 != df列数 ``` 4. **CSV解析异常(引用[1])** - 读取CSV时,行内列数不一致(如空行、格式错误): ```scala spark.read.csv("file.csv") // 某行列数不足 ``` --- #### 解决方案 ##### 1. 避免共享可变状态 - **使用局部变量**替代共享`ArrayBuffer`: ```scala rdd.map { x => val localBuffer = ArrayBuffer[Int]() // 每个Task独立实例 localBuffer += x localBuffer } ``` ##### 2. 使用线程安全集合 - 换用`java.util.concurrent.CopyOnWriteArrayList`: ```scala import java.util.concurrent.CopyOnWriteArrayList val safeList = new CopyOnWriteArrayList[Int]() rdd.foreach(x => safeList.add(x)) ``` ##### 3. 检查分区数据量 - 添加分区大小校验: ```scala rdd.mapPartitions { iter => val arr = iter.toArray if (arr.length > 100) arr(100) else -1 // 安全访问 } ``` ##### 4. 修复Schema不一致 - 确保DataFrame与目标表结构匹配: ```scala // 显式指定字段 df.select("col1", "col2").write.insertInto("table") ``` ##### 5. 处理CSV解析问题 - 使用Spark CSV选项处理异常行: ```scala spark.read .option("mode", "DROPMALFORMED") // 忽略格式错误行 .option("header", true) .csv("file.csv") ``` --- #### 调试建议 1. **检查堆栈信息** 定位异常发生在Driver端还是Executor端: - Driver端:共享变量问题 - Executor端:分区数据问题 2. **缩小数据范围** 用`sample()`或`limit()`复现问题: ```scala rdd.sample(false, 0.1).foreach(...) ``` 3. **增加日志** 在`mapPartitions`中记录分区大小: ```scala rdd.mapPartitions { iter => logger.info(s"Partition size: ${iter.size}") ... } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值