Scala函数和方法比较

关于function和method,Scala规范中给出抽象的定义,理解它有助于深度领悟scala的编程思想,也能体会到function和method的本质区别。
首先要理清几个名词的关系:函数声明(Function declaration),函数值(Function value),方法类型(Method type)和方法值(Method value)。

函数声明 = 方法类型 + 函数体(表达式或者代码块)。重要的是,函数声明也就是方法(method),方法类型是它的组成部分,它给出了方法的描述(包括方法名和签名),但它不会直接执行。
Method = Function declaration = method type + body
Method type = just define method without value

函数类型定义了大量的函数抽象,用于生成函数值,而函数值就是函数(function)。函数类型本质是trait,它包含一个抽象apply方法,如下。

函数包括了匿名函数和方法值,而方法类型可以转换为方法值。
Function type = trait FunctionN with apply method
Function value = function = object instantiated by a class extends from FunctionN.

上面的描述是理论基础,在scala实践中函数(function)和方法(method)也是不同的。
函数本质是对象,是实现了 FunctionN trait的类的实例。Scala提供了各种不同参数数量的trait,如Function0,Function1…。而方法属于定义范畴,只有在调用的时候才会执行。
函数使用val定义,而方法使用def定义。所以函数在定义的时候就会执行,并开辟内存空间,大量的函数必定会产生内存问题,而方法则是在每次调用的时候才会执行。可以见函数更像一个变量,而方法则属于定义,更多用在类中实现功能。
方法可以转换为函数,而函数却不能转换为方法。方法转函数的方法是[method type] _,如下代码。

如上所说,函数是实例化的对象,所以它是有方法的,而方法则没有。

对于方法转函数,它的本质就是生成实例化的trait对象,所以每次执行会开辟不一样的内存空间,生成不同的对象。

Scala为函数的定义和调用提供了语法糖(Syntactic sugar),简化了函数的语法,当然也可以用完整的函数来定义和调用。如下代码,可以用lamda表达式来定义函数,也可以自己实现Function trait定义函数;可以用括号()来调用函数,也可以用apply方法调用函数,他们的效果是一样的。

方法支持类型参数(type-parameterized),即不指明参数的数据类型(可变类型),而函数语法糖不支持,因为函数要立刻执行,必须明确参数类型,而方法在调用的时候才会执行,此时指定参数是可以接受的。

如果函数想支持可变类型参数,必须自己实现trait,而不能使用lamda表达式。

函数定义可以有两种方式,一种是上面演示的匿名函数(anonymous function, 可以叫 => lamda表达式,也可以叫function literals),另一种是使用类型签名(type signature),如下代码。

参考:
http://jim-mcbeath.blogspot.com.au/2009/05/scala-functions-vs-methods.html
https://www.scala-lang.org/files/archive/spec/2.11/04-basic-declarations-and-definitions.html#default-arguments
https://www.scala-lang.org/files/archive/spec/2.11/03-types.html#function-types

Scala函数和方法比较

DOCKER系列 – 内置KUBERNETES (二)

上一章介绍了内置kubernetes的docker实验版的安装,具备了项目部署的基本环境,这一章将演示在k8s上部署的具体步骤。

Docker store官网提供了丰富的学习镜像,这里使用docker kubernetes官方著名的示例words演示。

1. 配置文件

创建名为stack.yml的docker compose文件,将如下内容复制到文件中保存。这里使用dockerdemos下的lab-web,lab-words和lab-db搭建words应用。

2. 部署应用

执行如下代码,它会自动下载并部署到kubernetes中。
docker stack deploy –compose-file stack.yml demo

3. 查看部署

执行如下命令查看示例部署情况,可以看到它按照docker compose文件成功部署words到kubernetes。

4. 浏览器查看

在浏览器输入http://localhost/,看到如下页面说明成功安装。

5. 删除部署

通过如下命令停止和移除部署,通过上面演示过的命令可以看出demo已经被移除,并且网页也无法打开。

从上面简单的示例可以看出,集成在docker中的kubernetes,通过docker命令就可以实现在k8s上的部署。

参考:
https://docs.docker.com/docker-for-mac/kubernetes/
https://media-glass.es/docker-and-kubernetes-40ab7d01909f
https://rominirani.com/tutorial-getting-started-with-kubernetes-with-docker-on-mac-7f58467203fd

DOCKER系列 – 内置KUBERNETES (二)

DOCKER系列 – 内置Kubernetes (一)

最近docker发布新的实验版(Edge),最重要的特性就是集成了kubernetes,当然稳定版(stable)是不具备这个功能的。这样就省去了手动安装kubernetes繁琐的步骤,轻松的实现在Mac上搭建基于kubernetes+docker的容器云。
最权威的教程来自一个docker团队发布的YouTube视频,网上也涌现出多篇详细的文章,不过处于对docker拥抱kubernetes的支持,我还是想做一次重复造轮子的事情。

本章首先完成docker edge的下载,安装和启动。

1. 下载

进入官网下载docker 社区实验版,如下图。根据官网描述,只有Docker CE Edge 17.12及以上才支持,所以下载需注意。

2. 安装

双击下载的Docker.img文件按步骤安装,完成后打开docker,它会出现在导航栏上。
点击导航栏上的docker图标,会看到Docker is running,说明docker已经启动,而kubernetes默认关闭。然后点击About Docker,可以看到kubernetes的版本是1.8.2。

3. 启动Kubernetes

再次点击Docker图标,然后点击Preferences…,打开docker配置窗口,点击最后一个标签打开kubernetes的配置,可以看到它默认是关闭的。
点击Enable Kubernetes,会出现如图,点击install它会开始下载和安装Kubernetes cluster和kubectl,安装过程会持续一段时间,完成后会出现安装成功的弹窗。

再次打开docker kubernetes配置,勾选Show system containers,这样在查看容器时kubernetes内置容器也会列出。

4. 查看Kubernetes

分别执行如下命令检查kubernetes安装情况。

可以看到kubernetes默认安装了一个单节点集群docker-for-desktop

可以看到很多container来自google k8s,这也是因为上面勾选了Show system containers

5.安装可视化界面

虽然kubernetes的操作都可以通过命令行kubectl完成,但如果希望使用UI界面查看,就需要安装kubernetes dashboard。执行如下命令安装dashboard

安装完成后,通过如下命令查看dashboard安装情况,如下图。

输入如下命令启动dashboard服务

通过在浏览器中输入如下url访问dashboard

它会弹出登陆窗口,因为这里是proxy访问,点击skip直接进入dashboard

默认是没有deployments,这里是因为我已经部署了测试项目。

DOCKER系列 – 内置Kubernetes (一)

Scala函数的参数类型

方法的参数的调用有两种格式:按值(by-name)传递按名(by-name)传递。值参数是函数默认的参数格式,函数调用的时候所有值参数会立刻执行,而名参数在函数定义中需要用=>指定,它在函数调用的时候不会被立刻执行,只有在函数体内应用的时候才会执行。可以将按值传递参数想象成val,按名传递参数想想为def。

下面通过一个例子对比看两种参数传递方式。对于两种参数传递方式,如果参数是普通的数据类型(例如这里的字符串)返回结果是一样的,但如果参数是方法或者对象就会不一样了。对使用按值传递参数的函数mood,注意红框区域,两个println会在调用mood的时候执行,产生两个输出,而类似地将mood改为按名传递参数的函数,println不会在函数调用的时候立刻执行,会在函数体内需要的地方执行(例如,现在是2018-01-16,周二,那么只有println(“sad”)执行)。

正确使用两种参数类型不仅能解决函数调用中按需执行的问题,也能提高程序性能,减少内存消耗。

Scala函数的参数类型

Scala def和val比较

Scala 提供了四种变量定义的方式:defvallazy valvar。def定义的变量像是“占位符”,变量定义并不会执行,在每次调用变量的时候会执行;而val变量在定义的时候就会执行,且只执行一次;lazy val和val一样只会执行一次,但它是在使用的时候执行,不是在定义的时候。前面的def,(lazy) val用于定义不可变量,而var用于定义可变量,除此之外它和val一样的,所以下面demo不对var做演示。

一般情况下,def用于定义方法,val用于定义不可变量,var用于定义变量

先看一个简单的例子,它用于生成时间戳,他们分别使用val,def和lazy val定义。

从运行结果可以看出,val定义的timestamp1在定义的时候就被执行,且使用timestamp1定义的List中每个元素值是一样的。def定义的timestamp2在定义的时候并没有执行(因为没有执行println),然而在使用timestamp2定义的List中每个元素值是连续的三个值(因为线程sleep了1毫秒),且打印出现了三次,说明定义被执行了三次。对比前两个,lazy val定义的变量timestamp3并没有直接执行,而是在定义List的时候才执行,并且只执行了一次,这说明lazy val还是val,只是“聪明的“等到使用的时候才执行。

下面再看一个例子,isDivisibleBy用于生成一个函数(Int=>Boolean),它表示某数字是否可以被整除。

从代码执行结果可以看出,isEven1在定义的时候isDivisibleBy方法就执行了,所以isEven1的调用就等价于i%k==0,而isEven2在每次执行的时候才会调用isDivisibleBy方法(从println执行可以看出),所以isEven2的调用就等于isDivisibleBy(k)(i)。

def和val的概念在scala中很重要,它是理解方法和函数,值参数和名参数的基础,后面会有介绍。

Scala def和val比较