programming-languages学习笔记–第8部分

*/-->

pre.src {background-color: #292b2e; color: #b2b2b2;}

pre.src {background-color: #292b2e; color: #b2b2b2;}

pre.src {background-color: #292b2e; color: #b2b2b2;}

pre.src {background-color: #292b2e; color: #b2b2b2;}

pre.src {background-color: #292b2e; color: #b2b2b2;}

pre.src {background-color: #292b2e; color: #b2b2b2;}

pre.src {background-color: #292b2e; color: #b2b2b2;}

pre.src {background-color: #292b2e; color: #b2b2b2;}

pre.src {background-color: #292b2e; color: #b2b2b2;}

pre.src {background-color: #292b2e; color: #b2b2b2;}

pre.src {background-color: #292b2e; color: #b2b2b2;}

pre.src {background-color: #292b2e; color: #b2b2b2;}

pre.src {background-color: #292b2e; color: #b2b2b2;}

programming-languages学习笔记–第8部分

1 ruby

纯面向对象:所有的值都是对象(包括数字)

  • 基于类:每个对象都有一个类
  • 动态类型
  • 方便的反射:运行时查看对象
  • 动态特性:可以在执行时修改对象或类
  • 块和库鼓励很多闭包惯用法
  • 脚本语言的语法,作用域规则和语义

      动态类型 静态类型
    函数式(FP) Racket SML
    面向对象(OOP) Ruby Java,等

2 类与对象

在ruby中:

  • 所有的值都引用一个对象
  • 对象通过方法调用交互,也叫做消息传递
  • 每个对象有它自己的(私有)状态,只有对象的方法可以直接访问或更新这个状态。
  • 每个对象都是一个类的实例
  • 对象的类决定了对象的行为。类包含决定对象如何处理消息的方法定义。

3 对象,类,方法,变量

class Name
def method_name1 method_args1
#expression1
end
def method_name2 method_args2
#expression2
end
#...
end

创建和使用对象:

  • ClassName.new 创建一个类ClassName的新对象
  • e.m求值e为一个对象,然后调用它的方法m,也可以称为发送消息m,也可以写为e.m()

变量:

  • 方法可以使用局部变量,以字母开头
  • 不需要声明
  • 变量是可变的 x=e
  • 变量可以用在"top-level"或REPL中
  • 变量的内容总是引用一个对象,因为所有的值都是对象

self:

  • 在Ruby中,self是一个特殊的关键字/变量
  • 引用当前对象:执行方法的对象
  • 可以调用同一个对象的其它方法:self.m(…),可以用语法糖: m(…)
  • 可以使用self传递/返回/存储整个对象
  • 与Java/C#/C++的this相同

4 对象状态

状态只能通过对象的方法直接访问。

状态由实例变量组成(也叫做字段):

  • 语法:以@开头,比如@foo
  • 使用=赋值
  • 使用一个未赋值的状态不会产生错误,产生一个nil对象。

别名:

  • 创建一个对象返回一个新对象的引用,与其它对象的状态不同
  • 赋值=创建一个别名,别名表示同一个对象,拥有同样的状态。

初始化:

  • 方法名为initialize的方法是特殊的:

    • 在一个新对象的new返回前调用
    • new的参数传递给initialize
    • 用于创建对象不变式
    • 类似于Java/C#等的构造器
  • 在initialize中初始化实例变量是好的编程风格
    • 只是一个惯例
    • 在Ruby中,同一个类的不同实例可以拥有不同的实例变量

类变量:

  • 整个类共享的变量
  • 类的所有实例都可以访问
  • 称为类变量,使用@@开头,比如@@foo
  • 不常用,但有时很有用

类常量:

  • 语法:以大写字母开头,比如Foo
  • 最好不要去改变
  • 在类C的外面可以通过C::Foo访问

类方法(Java/C#中的静态方法):

  • 语法: def self.method (args) … end
  • 使用: C.method(args)
  • 属于类的一部分,不属于某个具体实例

5 Visibility

谁能访问什么.

对象状态总是私有的。可以通过getters/setters访问器公开访问。
访问器语法糖:

  • 只定义getters: attr_reader :foo, :bar, …
  • 定义getters和setters: attr_accessor :foo, :bar, …
  • getters/setters只是方法

方法的可见性:private, protected,public。方法默认是public。

class Foo
# 默认方法是public protected
# 现在定义方法是protected private
# 现在定义方法是private
end

如果一个方法m是私有的,则只能通过m或m(args)调用,不能用self.m调用

6 万物皆对象

 +
# 等价于
.+() .abs
.nonzero?
x = if > then else - end
x.abs
nil # nil是一个对象
.nil?
nil.nil?
if nil then puts "A" else puts "B" end # nil被认为是false

所有的代码都是方法:

  • 你定义的所有方法都是一个类的一部分
  • top-level方法(文件中或REPL)只是添加到Object类
  • 因为你定义的所有类都是Object的子类,因此都继承top-level方法
  • 因此可以在程序的任何地方调用这些方法
  • 除非一个类定义了同名方法覆盖了这个方法。

所有的对象都有methods,class方法。可以用来在运行时查找一个对象可以做什么,并作出反应,叫做reflection(反射)。

类是一个Class对象。

7 类定义是动态的

Ruby程序可以在运行时添加修改方法。

动态特性引起一些有趣的语义问题,比如:

  • 首先创建一个类C的实例,x = C.new
  • 现在修改C中的方法m
  • 现在调用x.m

在Ruby中,调用的是新定义的方法。

8 Duck Typing

def mirror_update pt
pt.x = pt.x * -
end

这个方法接受一个对象,只要这个对象有x的访问器函数就可以正常调用,不管pt是哪个类的实例。

9 数组

get:a[i], set:a[i]=e

Ruby的数组非常灵活。

10 Blocks

等同于closures。

可以对任何消息传递0或一个block。语法: {e}, {|x| e},{|x,y| e},也可以用begin…end替换{}。

使用yield调用block:

def silly a
puts (yield a)
(yield a) + (yield )
end
public :silly
.silly(){ |b| b* }

11 Procs

blocks不是对象,只能yield。但可以将blocks转换为真实的closures。
闭包是Proc类的实例,使用方法call调用。

Object对象的lambda方法,接受一个block,返回一个Proc对象。

inc = lambda {|x| x + }
inc.call
6

12 Hashes和Ranges

哈希表{}

h = {"SML" => , "Racket" => , "Ruby" => }
puts h
h2 = {:sml => , :racket => , :ruby => }
puts h2
{"SML"=>7, "Racket"=>12, "Ruby"=>42}
{:sml=>7, :racket=>12, :ruby=>42}

Ranges: 1..100,
这里也是duck typing:

def foo a
a.count { |x| x*x < }
end puts foo [,,,]
puts foo (..)
3
5

13 Subclassing

子类,继承和覆盖。Ruby中不会继承父类的字段定义,因为实例变量不是类定义的一部分,每个对象实例创建它自己的实例变量。

Ruby中不指定superclass,父类就是Object,superclass影响类定义:

  • 继承superclass的所有方法,可以根据需要override方法定义
  • 每个对象的class方法返回这个对象的类,一个类也是一个对象,这个类的class就是Class。
  • 每个对象有is_a?和instance_of?方法,instance_of?只有在对象是某个类的实例的情况下才为真,子类不为真。
class ColorPoint < Point
end

14 覆盖和动态派发

至此,对象与闭包并没有很大的不同:

  • 对象的多个方法与闭包的"call me"
  • 对象的显式实例变量与函数定义时的环境
  • 继承避免辅助函数或代码copy
  • 简单的覆盖只是替换方法

但是有一个最大的不同:覆盖可以在父类中定义方法调用子类中的方法。
这个语义有很多种叫法:dynamic dispatch,late binding,virtual method calls.

15 方法查找的精确定义

查找一些东西经常是一个编程语言语义的本质。例如在ML和Racket中,查找变量的规则导致了词法作用域和函数闭包的正确处理。在Racket中,3中不同形式的let表达式表示了在子表达式中查找变量的不同语义。

在Ruby中,方法和块(blocks)中的局部变量查找规则与ML和Racket并无不同,除了使用前不需要预先声明。 但是实例变量,类变量和方法的查找依赖绑定到self的对象,并且self是特殊的。

在任何环境中,self映射为一些对象,当前执行方法的这个对象。查找实例变量@x时,使用绑定到self的对象,每个对象有它自己的状态,我们使用self的状态。查找类变量@@x时,使用绑定到self.class的对象的状态去代替。查找方法m更复杂一点,求值一个方法调用e0.m(e1,…,en):

  • 求值e0,e1,…,en到值,也就是对象obj0,obj1,…,objn。
  • 获得obj0的class,每个对象在运行时知道它自己的类.可以认为class是obj0的状态的一部分。
  • 假定obj0是类A,如果A中定义了m,则调用这个方法。否则递归查找A的父类中是否定义方法m.如果找不到方法m,则引发"method missing"错误。在Ruby中,将调用method_missing方法,并重新开始在A和它的父类中查找method_missing,但是大部分类没有定义method_missing,并且Object定义了它,调用它会引发我们希望的错误。
  • 现在找到了要调用的方法,如果这个方法有形式参数x1,x2,…,xn,则求值环境映射为x1到obj1,x2到obj2等。但是这里有一个OOP与函数式编程的本质不同:我们在环境中总是拥有self。求值方法体时,self绑定到obj0,即接收消息的这个对象。

上面描述的在被调用者内部绑定self的含义等同于"late-binding","dynamic dispatch","virtual method calls"。它是Ruby和其它OOP语言语义的核心。它表示当方法m的内部在self上调用一个方法(比如self.some_method 34或some_method 34)时,我们使用obj0的类来解析方法some_method。不一定是我们正在执行的方法的类。

这个语义还有几点:

  • Ruby的mixins增加了查找规则的复杂度,所以上面的规则忽略了mixins
  • 这个语义比ML/Racket的函数调用要复杂。但是复杂并不意味着它更好或更差,仅表示语言定义有更多需要描述的细节。这个语义显然对很多人都很有用。
  • Java/C#有更复杂的方法查找规则。它们有这里描述的动态派发,但是它们也有静态重载(static overloading),一个类可以有接受不同类型(或个数)的参数的多个重名方法。

16 动态派发对比闭包

fun even x = if x =  then true else odd (x - )
and odd x = if x = then false else even (x - ) (* 不会修改odd的行为,因为odd在定义的环境中查找even *)
fun even x = false (* 用一个更优化的版本替换even,odd是无法获得这个优化实现的好处 *)
fun even x = (x mod ) =

在OOP中,可以使用子类化,覆盖,和动态派发,通过覆盖even来修改odd的行为:

class A
def even x
if x == then true else odd(x-) end
end def odd x
if x == then false else even(x-) end
end
end class B < A
def even x #也会修改B的odd!
x % ==
end
end

现在执行B.new.odd 17会执行的更快,因为odd会调用B中的even–因为绑定到环境中的self。但它也有缺点,不能只看一个类A就知道调用代码有什么样的行为。在子类中,如果有人覆盖了even而不知道它会修改odd的行为怎么办?

基本上,对可能被覆盖的方法的任何调用都要非常仔细地考虑。通常最好用不能被覆盖的私有方法。 然而,覆盖和动态派发是面向对象编程与函数式编程最大的区别。

作者: ntestoc

Created: 2019-01-09 三 13:25

programming-languages学习笔记--第8部分的更多相关文章

  1. CUDA Programming Guide 学习笔记

    CUDA学习笔记 GPU架构 GPU围绕流式多处理器(SM)的可扩展阵列搭建,每个GPU有多个SM,每个SM支持数百个线程并发执行.目前Nvidia推出了6种GPU架构(按时间顺序,详见下图):Fer ...

  2. Programming Erlang 学习笔记(一)

    入门 启动Shell 在cmd中输入命令”erl”,百分号(%)表示一个注释的开始,从百分号开始到这行结束的所有文本都被看做是注释. 一个完整的命令需要以一个句点和一个回车结束. 退出erlang的命 ...

  3. UIView Programming Guide学习笔记

    |View |Creating and Configuring View Objects |Creating and Managing a View Hierarchy |Adjusting the ...

  4. The C++ Programming Language 学习笔记 第7章 函数

    1.关于内联函数(inline)      借用一下书中的例子. inline int fac(int n) { ) ? :n*fac(n-); }      inline描述符给编译器一个提示,要求 ...

  5. The C++ Programming Language 学习笔记 第6章 表达式和语句

    1.关于strcpy函数. 书中说c风格的字符串尽量少用,strcpy这样的函数应该也要少用.这里讲这个函数主要是要通过本章课后练习第十题来讲一下前面提及的要点.巩固一下前几章的知识.写了一段,本来感 ...

  6. The C++ Programming Language 学习笔记 第5章 指针、数组和结构

    1.关于输出指向字符的指针的值. 现在定义,char c='a',char* pc=&c.在C中,输出该值只需要printf("%p\n",pc);而在C++中,如果cou ...

  7. The C++ Programming Language 学习笔记 第四章 类型和声明

    1.关于main 函数中的 return 0 C99标准中,main 函数的返回值类型必须是 int ,这样返回值才能传递给程序的激活者(如操作系统).如果 main 函数的最后没有写 return ...

  8. 3D Game Programming withDX11 学习笔记(一) 数学知识总结

    在图形学中,数学是不可或缺的一部分,所以本书最开始的部分就是数学知识的复习.在图形学中,最常用的是矢量和矩阵,所以我根据前面三个章节的数学知识,总结一下数学知识. 一.矢量 数学中的矢量,拥有方向和长 ...

  9. Learning ROS for Robotics Programming Second Edition学习笔记(十) indigo Gazebo rviz slam navigation

    中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 moveit是书的最后一章,由于对机械臂完全不知,看不懂 ...

  10. Learning ROS forRobotics Programming Second Edition学习笔记(八)indigo rviz gazebo

    中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS forRobotics Pro ...

随机推荐

  1. 插入sql返回主键id

    <insert id="insertSelective" parameterType="com.xxx.model.XDetail" useGenerat ...

  2. UOJ#410. 【IOI2018】会议

    传送门 首先可以设 \(f[l][r]\) 表示 \([l,r]\) 的答案 设 \(x\) 为区间 \([l,r]\) 的最大值的位置,那么 \(f[l][r] = min(f[l][x-1]+h[ ...

  3. nginx的启动和关闭

    nginx的启动和关闭nginx -h 查看帮助信息nginx -v 查看Nginx的版本号nginx -V 显示Nginx的版本号和编译信息start nginx 启动Nginxnginx -s s ...

  4. 【canvas系列】用canvas实现一个colorpicker(类似PS的颜色选择器)

    每个浏览器都有自己的特点,比如今天要做的colorpicker就是,一千个浏览器,一千个哈姆雷特,一千个colorpicker.今天canvas系列就用canvas做一个colorpicker. ** ...

  5. elasticsearch 多列 聚合(sql group by)

    文档数据格式 {"zone_id":"1","user_id":"100008","try_deliver_t ...

  6. [转]Linux内核最新的连续内存分配器(CMA)——避免预留大块内存

    http://blog.csdn.net/21cnbao/article/details/7309757 在我们使用ARM等嵌入式Linux系统的时候,一个头疼的问题是GPU,Camera,HDMI等 ...

  7. 1finally与return、exit()

    public class TestException { public static void main(String[] args) { String[] str = {"1", ...

  8. 优化 ExpressRoute 路由

    当你有多个 ExpressRoute 线路时,可以通过多个路径连接到 Azure.结果就是,你所采用的路由可能不是最理想的 - 也就是说,你的流量可能会经历较长的路径才能到达 Azure,而 Azur ...

  9. C# Json转对象

    第一步,项目添加negut的搜索Newtonsoft.Json,安装第一个:如图所示: 安装以后,自动引用. private ObservableCollection<Traffic> m ...

  10. 非定制UIImagePickerController的使用

    非定制UIImagePickerController的使用 效果: 源码: // // ViewController.m // ImagePic // // Created by XianMingYo ...