scala中的中缀表达在3个地方存在:操作符、模式匹配、类型声明
1)中缀操作符最常见: a + b
或 x foo y
里的+
和foo
都是中缀操作符(严格的说scala里没有操作符,其实都是方法)。
其中需要注意的一点是名称最后以“:”结尾的,调用顺序与普通中缀操作符想反:从右往左。比如h :: t
实际是 t.::(h)
,它的意义在于可以让表达看起来更“顺眼”一些。比如我们自定义一个:
// 一个Cache单例,提供了 >>: 方法
scala> object Cache { def >>:(data:String) { println(data) } }
defined module Cache
scala> "myData" >>: Cache
myData
上面的方法很直观,让人一看就容易理解是把数据追加到cache中。还可以支持连续操作,稍修改一下:
scala> object Cache { def >>:(data:String):Cache.type = { println(data); Cache } }
defined module Cache
scala> "data2" >>: "data1" >>: Cache
data1
data2
res6: Cache.type = Cache$@45d2d12f
实际中,最常见的从右往左结合的中缀操作符是 List
的 ::
方法
scala> val list = List()
list: List[Nothing] = List()
scala> "A" :: "B" :: list
res4: List[String] = List(A, B)
2) 当一个高阶类型有2个参数化类型,比如
scala> class Foo[A,B]
在声明变量类型时,也可以用 A Foo B
中缀形式来表达,Foo
也称为中缀类型
scala> val x: Int Foo String = null
即 Int Foo String
等同于 Foo[Int,String]
3) 包含两个参数的构造器,在模式匹配时也可以用中缀表达
scala> case class Cons(a:String,b:String)
defined class Cons
scala> val x = Cons("one", "two")
x: Cons = Cons(one,two)
scala> x match { case "one" Cons "two" => println("ok") }
ok
即 "one" Cons "two"
等同于 Cons("one", "two")
对于2)和3)似乎也体现了某种一致性,不过暂对中缀类型的好处没有体会,对于类型系统中的这种风格也还不了解,暂不做比较。
现实中有个很容易迷惑人的地方,常用的 List
,即存在了一个::
方法, 也存在了一个::
名字的case类(List的子类)。
所以,构造一个list可以通过::
的伴生对象提供的工厂方法,也可以通过Nil的::
方法
scala> val l = ::("A",Nil) // 这里::是伴生对象, 相当于 ::.apply()
l: scala.collection.immutable.::[String] = List(A)
scala> val l2 = "A" :: Nil // 这里::是方法, 相当于 Nil.::()
l2: List[String] = List(A)
scala> l == l2
res1: Boolean = true
在模式匹配时,可以用中缀形式:
// 这里::是伴生对象,相当于 ::.unapply()
case head :: tail => …
所以::
在不同的场景,意思不同,有时是方法名,有时是伴生对象名。这个设计,最初了解时很爱吐槽。但它提供了一种看上去很一致的表象,在中缀形式下都是表示”头”与”尾”的连接。
开始是挺迷惑的,后来发现模式匹配用的是:: (case class), 其它多数都是::方法。
补充:
case class Cons(a:String,b:String) 因为有unapply返回
object 示例(伪):
val Head(h) & State(code) & Body(b) = http
你的示例是想表达与 unapply 有关系的用法吗?没看明白
前几天看到代码里有 /: :\ 没搞明白为何用的时候不同, 原来是 : 规则
其实 |> 和 <| 直观一点,
博主您好,想请教您文章中提到的 case class ::的伴生对象及对应的unapply方法在哪个文件里。我找了下case class ::所在的代码文件,好像并没有找到。谢谢!
对于case class,编译器会对其生成一个伴生对象,这个对象里会提供unapply方法。
➜ cat C.scala implements scala.Serializable { unapply(C);
case class C(name:String)
➜ scalac C.scala
// 这里编译器生成两个class文件,一个是C.class,一个是C$.class
➜ javap C$
Compiled from “C.scala”
public final class C$ extends scala.runtime.AbstractFunction1
public static final C$ MODULE$;
public static {};
public final java.lang.String toString();
public C apply(java.lang.String);
public scala.Option
public java.lang.Object apply(java.lang.Object);
}
用scalap看更清晰:
➜ scalap C
case class C(name: scala.Predef.String) extends scala.AnyRef with scala.Product with scala.Serializable {
val name: scala.Predef.String = { /* compiled code */ }
def copy(name: scala.Predef.String): C = { /* compiled code */ }
override def productPrefix: java.lang.String = { /* compiled code */ }
def productArity: scala.Int = { /* compiled code */ }
def productElement(x$1: scala.Int): scala.Any = { /* compiled code */ }
override def productIterator: scala.collection.Iterator[scala.Any] = { /* compiled code */ }
def canEqual(x$1: scala.Any): scala.Boolean = { /* compiled code */ }
override def hashCode(): scala.Int = { /* compiled code */ }
override def toString(): java.lang.String = { /* compiled code */ }
override def equals(x$1: scala.Any): scala.Boolean = { /* compiled code */ }
}
object C extends scala.runtime.AbstractFunction1[scala.Predef.String, C] with scala.Serializable {
def this() = { /* compiled code */ }
final override def toString(): java.lang.String = { /* compiled code */ }
def apply(name: scala.Predef.String): C = { /* compiled code */ }
def unapply(x$0: C): scala.Option[scala.Predef.String] = { /* compiled code */ }
}
所以您的意思是在pattern match List的时候,比如:
List(1, 2, 3) match {
case x :: y => “ok”
}
case class x::y是通过编译器自动生成的unapply匹配List(1, 2, 3)的?
但是作为一个scala新手,我感到有些困惑。我能理解::自动生成的unapply会匹配::实例,但是不能理解为什么也能匹配一般的List对象(::是List的子类,但List不是::的子类),比如List(1, 2, 3)。
请博主赐教,谢谢!
List(1,2,3) 是调用 List.apply(1,2,3) 产生的结果是一个 :: 对象
scala> val l = List(“A”)
l: List[String] = List(A)
scala> l.getClass
res0: Class[_ <: List[String]] = class scala.collection.immutable.$colon$colon 看到这个class名称 $colon$colon 就是 :: 类
明白了,谢谢博主答复。所以Scala中一般的List实例其实都是::实例对吗?
对,大多数你见到的非空的List基本都是::实例,List还有另外一个子类Nil,表示空。
sealed trait Stream[+A]
{
def foldRight[B](z: => B)(f: (A, => B) => B): B =
this match {
case Cons(h, t) => f(h(), t().foldRight(z)(f))
case _ => z
}
}
case object Empty extends Stream[Nothing]
case class Cons[+A](h: () => A, t: () => Stream[A]) extends Stream[A]
scala 函数式变成第五章,这个foldRight中的f:(A, =>B)=>B,中的逗号怎么理解
逗号就是分隔符,后边的=>B 表示一个传名参数类型
那个写错了,1==12 返回false
那是英文L和L2.