Scala在线运行

版本:

所属目录
点击了解高性能代码运行API
运行结果
教程手册
代码仓库
极速运行
终端运行
图形+终端

                        
以下是用户最新保存的代码
文本文件翻译 发布于:2024-03-15 14:27 九九乘法表 发布于:2024-03-08 20:22 工资计算函数 发布于:2024-03-08 20:10 薪水扣税计算 发布于:2024-03-08 20:04 乘法口诀表 发布于:2024-03-08 20:02 实际工资计算 发布于:2024-03-08 19:53 九九乘法表 发布于:2024-03-08 19:53 九九乘法表 发布于:2024-03-08 19:50 九九乘法表 发布于:2024-03-08 19:46 工资计算器 发布于:2024-03-08 19:43 工资函数计算 发布于:2024-03-08 19:41 02:工资计算函数的实现 发布于:2024-03-08 19:54 乘法口诀表 发布于:2024-03-08 19:41 工资计算函数的实现 发布于:2024-03-08 19:48 工资计算函数的实现 发布于:2024-03-08 19:51 01:编写代码实现1~9乘法口诀 发布于:2024-03-08 19:54 九九乘法表 发布于:2024-03-08 19:39 九九乘法口诀表 发布于:2024-03-08 19:38 实际工资计算 发布于:2024-03-08 19:45 乘法口诀表 发布于:2024-03-08 19:44 工资函数计算 发布于:2024-03-08 19:47 九九乘法表 发布于:2024-03-08 19:34 打印九九乘法表 发布于:2024-03-08 19:39 打印九九乘法表 发布于:2024-03-08 19:32 工资计算函数 发布于:2024-03-08 19:43 九九乘法表 发布于:2024-03-08 19:51 9*9乘法表scala 发布于:2024-03-08 19:43 demo4数组操作 发布于:2024-03-08 19:13 编程计算并输出下列级数的前n 项之和Sn,直到Sn 刚好大于或等于q为止,其中q 为大于0 的整数,其值通过键盘输入。例如, 若q 的值为50.0 , 则输出应为: Sn=50.416695 。请将源文件保存为exercise2-1.scala,在REPL 模式下测试运行,测试样例:q=1 时,Sn=2;q=30 时,Sn=30.891459;q=50 时,Sn=50.416695。 发布于:2024-03-04 22:55 成绩分析代码 发布于:2024-02-17 19:45 记录成绩代码 发布于:2024-02-17 17:15 # ## Scala学习 发布于:2024-02-17 16:12 Scala 水仙花 发布于:2023-12-25 19:51 我的第一个scala程序 发布于:2023-09-27 10:58 class ScalaTest1 { object ScalaClass { var a=10 val b=20 val c=a+b println(c) } } 发布于:2023-06-15 20:49 啊是公司的 发布于:2023-06-09 08:43 Scala - Determine whether a given integer number is prime in Scala 发布于:2023-04-23 18:49 输入一个整型数字,输出是否为奇数 发布于:2023-04-23 17:39 类对象(31-40) 发布于:2022-11-06 19:39 无论是继承abstract class或者混入trait,对于一个要实现的类来说,必须先用extends,剩下的用with,否则编译的时候就会出错。 发布于:2022-10-16 19:15 3.scala_3(循环) 发布于:2022-05-17 10:38 2.scala_2(条件判断) 发布于:2022-05-17 10:32 按市场是从 发布于:2022-03-22 14:24 person类 发布于:2022-03-04 21:46 第一个测试程序 发布于:2021-09-12 08:51 //The is first Demo My code 发布于:2021-09-10 11:22 scala 获取当前日期,日期的加减等 发布于:2020-12-29 14:38 [更多]
显示目录

类型和多态基础



学习嵌入式的绝佳套件,esp8266开源小电视成品,比自己去买开发板+屏幕还要便宜,省去了焊接不当搞坏的风险。 蜂鸣版+触控升级仅36元,更强的硬件、价格全网最低。

点击购买 固件广场

类型和多态基础

什么是静态类型?

按 Pierce 的话讲:“类型系统是一个语法方法,它们根据程序计算的值的种类对程序短语进行分类,通过分类结果错误行为进行自动检查。”

类型允许你表示函数的定义域和值域。例如,从数学角度看这个定义:

f: R -> N

它告诉我们函数“f”是从实数集到自然数集的映射。

抽象地说,这就是具体类型的准确定义。类型系统给我们提供了一些更强大的方式来表达这些集合。

鉴于这些注释,编译器可以静态地 (在编译时)验证程序是合理的。也就是说,如果值(在运行时)不符合程序规定的约束,编译将失败。

一般说来,类型检查只能保证不合理的程序不能编译通过。它不能保证每一个合理的程序都可以编译通过。

随着类型系统表达能力的提高,我们可以生产更可靠的代码,因为它能够在我们运行程序之前验证程序的不变性(当然是发现类型本身的模型 bug!)。学术界一直很努力地提高类型系统的表现力,包括值依赖(value-dependent)类型!

需要注意的是,所有的类型信息会在编译时被删去,因为它已不再需要。这就是所谓的擦除。

Scala 中的类型

Scala 强大的类型系统拥有非常丰富的表现力。其主要特性有:

  • 参数化多态性 粗略地说,就是泛型编程
  • (局部)类型推断 粗略地说,就是为什么你不需要这样写代码 val i: Int = 12: Int
  • 存在量化 粗略地说,为一些没有名称的类型进行定义
  • 视窗 我们将下周学习这些;粗略地说,就是将一种类型的值“强制转换”为另一种类型

参数化多态性

多态性是在不影响静态类型丰富性的前提下,用来(给不同类型的值)编写通用代码的。

例如,如果没有参数化多态性,一个通用的列表数据结构总是看起来像这样(事实上,它看起来很像使用泛型前的Java):

scala> 2 :: 1 :: "bar" :: "foo" :: Nil
res5: List[Any] = List(2, 1, bar, foo)

现在我们无法恢复其中成员的任何类型信息。

scala> res5.head
res6: Any = 2

所以我们的应用程序将会退化为一系列类型转换(“asInstanceOf[]”),并且会缺乏类型安全的保障(因为这些都是动态的)。

多态性是通过指定 类型变量 实现的。

scala> def drop1[A](l: List[A]) = l.tail
drop1: [A](l: List[A])List[A]

scala> drop1(List(1,2,3))
res1: List[Int] = List(2, 3)

Scala 有秩 1 多态性

粗略地说,这意味着在 Scala 中,有一些你想表达的类型概念“过于泛化”以至于编译器无法理解。假设你有一个函数

def toList[A](a: A) = List(a)

你希望继续泛型地使用它:

def foo[A, B](f: A => List[A], b: B) = f(b)

这段代码不能编译,因为所有的类型变量只有在调用上下文中才被固定。即使你“钉住”了类型 B:

def foo[A](f: A => List[A], i: Int) = f(i)

…你也会得到一个类型不匹配的错误。

类型推断

静态类型的一个传统反对意见是,它有大量的语法开销。Scala 通过 类型推断 来缓解这个问题。

在函数式编程语言中,类型推断的经典方法是 Hindley Milner 算法,它最早是实现在 ML 中的。

Scala 类型推断系统的实现稍有不同,但本质类似:推断约束,并试图统一类型。

例如,在 Scala 中你无法这样做:

scala> { x => x }
<console>:7: error: missing parameter type
       { x => x }

而在 OCaml 中你可以:

# fun x -> x;;
- : 'a -> 'a = <fun>

在 Scala 中所有类型推断是 局部的 。Scala 一次分析一个表达式。例如:

scala> def id[T](x: T) = x
id: [T](x: T)T

scala> val x = id(322)
x: Int = 322

scala> val x = id("hey")
x: java.lang.String = hey

scala> val x = id(Array(1,2,3,4))
x: Array[Int] = Array(1, 2, 3, 4)

类型信息都保存完好,Scala 编译器为我们进行了类型推断。请注意我们并不需要明确指定返回类型。

变性 Variance

Scala 的类型系统必须同时解释类层次和多态性。类层次结构可以表达子类关系。在混合 OO 和多态性时,一个核心问题是:如果 T’ 是 T 一个子类,Container[T’]应该被看做是 Container[T] 的子类吗?变性(Variance)注解允许你表达类层次结构和多态类型之间的关系:

名称 含义 Scala 标记
协变covariant C[T’]是 C[T] 的子类 [+T]
逆变contravariant C[T] 是 C[T’]的子类 [-T]
不变invariant C[T] 和 C[T’]无关 [T]

子类型关系的真正含义:对一个给定的类型T,如果T’是其子类型,你能替换它吗?

scala> class Covariant[+A]
defined class Covariant

scala> val cv: Covariant[AnyRef] = new Covariant[String]
cv: Covariant[AnyRef] = Covariant@4035acf6

scala> val cv: Covariant[String] = new Covariant[AnyRef]
<console>:6: error: type mismatch;
 found   : Covariant[AnyRef]
 required: Covariant[String]
       val cv: Covariant[String] = new Covariant[AnyRef]
                                   ^
scala> class Contravariant[-A]
defined class Contravariant

scala> val cv: Contravariant[String] = new Contravariant[AnyRef]
cv: Contravariant[AnyRef] = Contravariant@49fa7ba

scala> val fail: Contravariant[AnyRef] = new Contravariant[String]
<console>:6: error: type mismatch;
 found   : Contravariant[String]
 required: Contravariant[AnyRef]
       val fail: Contravariant[AnyRef] = new Contravariant[String]
                                     ^

逆变似乎很奇怪。什么时候才会用到它呢?令人惊讶的是,函数特质的定义就使用了它!

trait Function1 [-T1, +R] extends AnyRef

如果你仔细从替换的角度思考一下,会发现它是非常合理的。让我们先定义一个简单的类层次结构:

scala> class Animal { val sound = "rustle" }
defined class Animal

scala> class Bird extends Animal { override val sound = "call" }
defined class Bird

scala> class Chicken extends Bird { override val sound = "cluck" }
defined class Chicken

假设你需要一个以 Bird 为参数的函数:

scala> val getTweet: (Bird => String) = // TODO

标准动物库有一个函数满足了你的需求,但它的参数是 Animal。在大多数情况下,如果你说“我需要一个,我有一个的子类”是可以的。但是,在函数参数这里是逆变的。如果你需要一个接受参数类型 Bird 的函数变量,但却将这个变量指向了接受参数类型为 Chicken 的函数,那么给它传入一个 Duck 时就会出错。然而,如果将该变量指向一个接受参数类型为 Animal 的函数就不会有这种问题:

scala> val getTweet: (Bird => String) = ((a: Animal) => a.sound )
getTweet: Bird => String = <function1>

函数的返回值类型是协变的。如果你需要一个返回 Bird 的函数,但指向的函数返回类型是 Chicken,这当然是可以的。

scala> val hatch: (() => Bird) = (() => new Chicken )
hatch: () => Bird = <function0>

边界

Scala 允许你通过边界来限制多态变量。这些边界表达了子类型关系。

scala> def cacophony[T](things: Seq[T]) = things map (_.sound)
<console>:7: error: value sound is not a member of type parameter T
       def cacophony[T](things: Seq[T]) = things map (_.sound)
                                                        ^

scala> def biophony[T <: Animal](things: Seq[T]) = things map (_.sound)
biophony: [T <: Animal](things: Seq[T])Seq[java.lang.String]

scala> biophony(Seq(new Chicken, new Bird))
res5: Seq[java.lang.String] = List(cluck, call)

类型下界也是支持的,这让逆变和巧妙协变的引入得心应手。List[+T] 是协变的;一个 Bird 的列表也是 Animal 的列表。List 定义一个操作::(elem T)返回一个加入了 elem 的新的 List。新的 List 和原来的列表具有相同的类型:

scala> val flock = List(new Bird, new Bird)
flock: List[Bird] = List(Bird@7e1ec70e, Bird@169ea8d2)

scala> new Chicken :: flock
res53: List[Bird] = List(Chicken@56fbda05, Bird@7e1ec70e, Bird@169ea8d2)

List 同样定义了::B >: T 来返回一个List[B]。请注意B >: T,这指明了类型B为类型T的超类。这个方法让我们能够做正确地处理在一个List[Bird]前面加一个 Animal 的操作:

scala> new Animal :: flock
res59: List[Animal] = List(Animal@11f8d3a8, Bird@7e1ec70e, Bird@169ea8d2)

注意返回类型是 Animal。

量化

有时候,你并不关心是否能够命名一个类型变量,例如:

scala> def count[A](l: List[A]) = l.size
count: [A](List[A])Int

这时你可以使用“通配符”取而代之:

scala> def count(l: List[_]) = l.size
count: (List[_])Int

这相当于是下面代码的简写:

scala> def count(l: List[T forSome { type T }]) = l.size
count: (List[T forSome { type T }])Int

注意量化会的结果会变得非常难以理解:

scala> def drop1(l: List[_]) = l.tail
drop1: (List[_])List[Any]

突然,我们失去了类型信息!让我们细化代码看看发生了什么:

scala> def drop1(l: List[T forSome { type T }]) = l.tail
drop1: (List[T forSome { type T }])List[T forSome { type T }]

我们不能使用 T 因为类型不允许这样做。

你也可以为通配符类型变量应用边界:

scala> def hashcodes(l: Seq[_ <: AnyRef]) = l map (_.hashCode)
hashcodes: (Seq[_ <: AnyRef])Seq[Int]

scala> hashcodes(Seq(1,2,3))
<console>:7: error: type mismatch;
 found   : Int(1)
 required: AnyRef
Note: primitive types are not implicitly converted to AnyRef.
You can safely force boxing by casting x.asInstanceOf[AnyRef].
       hashcodes(Seq(1,2,3))
                     ^

scala> hashcodes(Seq("one", "two", "three"))
res1: Seq[Int] = List(110182, 115276, 110339486)
由JSRUN为你提供的Scala在线运行、在线编译工具
        JSRUN提供的Scala 在线运行,Scala 在线运行工具,基于linux操作系统环境提供线上编译和线上运行,具有运行快速,运行结果与常用开发、生产环境保持一致的特点。
yout