scala中的符号问题

1.集合操作

原文:https://blog.csdn.net/datadev_sh/article/details/79854273

val a = List(1,2,3)
val b = List(4,5,6)

符号 操作 结果 位置 解释
:: a :: b List(List(1, 2, 3), 4, 5, 6) 前插 把a当成一个元素,前插到b集合
+: a +: b List(List(1, 2, 3), 4, 5, 6) 前插 同上
:+ a :+ b List(1, 2, 3, List(4, 5, 6)) 后插 把b当成一个元素,后插到a集合
++ a ++ b List(1, 2, 3, 4, 5, 6) 拼接 a和b集合顺序合并
++: a ++:b List(1, 2, 3, 4, 5, 6) 拼接 同上
::: a::::b List(1, 2, 3, 4, 5, 6) 拼接 同上

2.泛型中的语法糖: “<:” , “>:” , “<%” , “=:=” , “<:<” , “<%<”, “+T”, “-T”

原文:https://blog.csdn.net/bobozhengsir/article/details/13023023

2.1 上下界约束符号 <: 与 >:

def using[A <: Closeable, B](closeable: A) (getB: A => B): B =
  try { 
    getB(closeable)
  } finally {
    closeable.close() 
  }

例子中A <: Closeable(java.io.Cloaseable)的意思就是保证类型参数A是Closeable的子类(含本类), >:反之。

2.2 协变与逆变符号+T与 -T

“协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型。e.g. String => AnyRef

“逆变”则是指能够使用派生程度更小的类型。e.g. AnyRef => String

【+T】表示协变,【-T】表示逆变

2.2.1 协变

借用一个例子来理解:
当我们定义一个协变类型List[A+]时,List[Child]可以是List[Parent]的子类型。
当我们定义一个逆变类型List[-A]时,List[Child]可以是List[Parent]的父类型。

class Animal {}
class Bird extends Animal {}
class Consumer[T](t: T) {
}
class Test extends App {
	val c:Consumer[Bird] = new Consumer[Bird](new Bird)
	val c2:Consumer[Animal] = c
}

c不能赋值给c2,因为Consumer定义成不变类型。

稍微改一下:

class Animal {}
class Bird extends Animal {}
class Consumer[+T](t: T) {
}
class Test extends App {
	val c:Consumer[Bird] = new Consumer[Bird](new Bird)
	val c2:Consumer[Animal] = c
}

因为Consumer定义成协变类型的,所以Consumer[Bird]是Consumer[Animal]的子类型,所以它可以被赋值给c2

2.2.2 逆变

继续上面得例子:

class Animal {}
class Bird extends Animal {}
class Consumer[-T](t: T) {
}
class Test extends App {
	val c:Consumer[Bird] = new Consumer[Bird](new Bird)
	val c2:Consumer[Hummingbird] = c
}

这里Consumer[-T]定义成逆变类型,所以Consumer[Bird]被看作Consumer[Hummingbird]的子类型,故c可以被赋值给c2。

2.3 视界 <%

Scala还有一种视图绑定的功能,如:

class Bird {def sing = {}}
class Toy {}
class Consumer[T <% Bird]() {
	def use(t: T) = t.sing
}

或者类型参数在方法上:

class Bird {def sing = {}}
class Toy {}
class Consumer() {
	def use[T <% Bird](t: T) = t.sing
}
class Test extends App {
	val c = new Consumer()
	c.use(new Toy)
}

它要求T必须有一种隐式转换能转换成Bird,也就是 T => Bird,否则上面的代码会编译出错:

No implicit view available from Toy => Bird.

加入一个隐式转换,编译通过。

import scala.language.implicitConversions
class Bird {def sing = {}}
class Toy {}
class Consumer() {
	def use[T <% Bird](t: T) = t.sing
}
class Test extends App {
	implicit def toy2Bird(t: Toy) = new Bird
	val c = new Consumer()
	c.use(new Toy)
}

2.4 广义类型约束符号 =:=, <:<, <%<

这些被称为广义的类型约束。他们允许你从一个类型参数化的class或trait,进一步约束其类型参数之一。

符号 含义
A =:= B 表示 A 必须是 B 类型
A <:< B 表示 A 必须是B的子类型 (类似于简单类型约束 <:)
A <%< B 表示 A 必须是可视化为 B类型, 可能通过隐式转换 (类似与简单类型约束 <%)

例子:

case class Foo[A](a:A) { // 'A' can be substituted with any type
    // getStringLength can only be used if this is a Foo[String]
    def getStringLength(implicit evidence: A =:= String) = a.length
}

结果:

scala> Foo("blah").getStringLength
res0: Int = 4

这个隐式的参数 evidence 由编译器提供,A =:=String表示证明A是String类型(PS:即使A可以隐式转换成String类型也不行),因此参数a就可以调用a.length 而编译器不会报错。

一旦我们使用其他不能转换成String类型的参数,就会报错,如下:

scala> implicit def charSeq2String(s: Seq[Char]) = s.mkString
charSeq2String: (s: Seq[Char])String
 
scala> Foo(Seq[Char]('a','b','c')).getStringLength
<console>:11: error: Cannot prove that Seq[Char] =:= String.
              Foo(Seq[Char]('a','b','c')).getStringLength
                                          ^

3. 函数参数的传名调用(call-by-name,:=>)和传值调用(call-by-value,:)

原文:https://blog.csdn.net/asongoficeandfire/article/details/21889375

类似于值传递(call-by-value,传值调用,符号" :")和引用传递(call-by-name,传名引用,符号 “:=>”)

例子:举一个例子,假设有一只酒鬼,他最初有十元钱,每天喝酒都会花掉一元钱。设他有一个技能是数自己的钱,返回每天他口袋里钱的最新数目。

package com.doggie
 
object Drunkard {
  //最开始拥有的软妹币
  var money = 10
  //每天喝掉一个软妹币
  def drink: Unit = {
    money -= 1
  }
  //数钱时要算上被喝掉的软妹币
  def count: Int = {
    drink
    money
  }
  //每天都数钱
  def printByName(x: => Int): Unit = {
    for(i <- 0 until 5)
      println("每天算一算,酒鬼还剩" + x + "块钱!")
  }
  //第一天数一下记墙上,以后每天看墙上的余额
  def printByValue(x: Int): Unit = {
    for(i <- 0 until 5)
      println("只算第一天,酒鬼还剩" + x + "块钱!")
  }
  
  def main(args: Array[String]) = {
    printByName(count)
    printByValue(count)
  }
}

我们使用成员变量money来表示酒鬼剩下的软妹币数量,每次发动drink技能就消耗一枚软妹币,在count中要计算因为drink消费掉的钱。我们定义了两种计算方式,printByName是传名调用,printByValue是传值调用。查看程序输出:

每天算一算,酒鬼还剩9块钱!
每天算一算,酒鬼还剩8块钱!
每天算一算,酒鬼还剩7块钱!
每天算一算,酒鬼还剩6块钱!
每天算一算,酒鬼还剩5块钱!
只算第一天,酒鬼还剩4块钱!
只算第一天,酒鬼还剩4块钱!
只算第一天,酒鬼还剩4块钱!
只算第一天,酒鬼还剩4块钱!
只算第一天,酒鬼还剩4块钱!
全部评论

相关推荐

秋招进行到现在终于能写总结了。完全没想到战线会拉这么长,过程会如此狼狈,不过更应该怪自己太菜了。好在所有的运气都用在了最后,也是有个去处。背景:双2本硕科班,无竞赛,本科一段研究所实习,硕士一段大厂暑期实习但无转正。技术栈是C++&nbsp;&amp;&nbsp;Golang,实习是客户端音视频(而且是鸿蒙端开发),简历两个C++项目一个Golang项目。主要投递岗位:后端,cpp软开,游戏服务端,测开,以及一些不拘泥于Java的岗位。从8月起总共投递123家公司,笔试数不清了,约面大约30家。offer/oc/意向:友塔游戏(第一个offer,面试体验很好,就是给钱好少南瑞继保(计算机科班点击就送(限男生),不...
乡土丁真真:佬很厉害,羡慕~虽然我还没有到校招的时候,也想讲一下自己的看法:我觉得不是CPP的问题,佬的背书双2,技术栈加了GO,有两段实习。投了123,面了30.拿到11个offer。这个数据已经很耀眼了。这不也是CPP带来的吗?当然也不止是CPP。至少来说在这个方向努力过的也会有好的结果和选择。同等学历和项目选java就会有更好的吗?我个人持疑问态度。当然CPP在方向选择上确实让人头大,但是我觉得能上岸,至于最后做什么方向,在我看来并不重要。至于CPP特殊,有岗位方向的随机性,java不是不挑方向,只是没得选而已。也希望自己以后校招的时候能offer满满
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务