zoukankan      html  css  js  c++  java
  • programming-languages学习笔记--第8部分

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

    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 万物皆对象

    3 + 4
    # 等价于
    3.+(4)
    
    3.abs
    3.nonzero?
    x = if 3 > 4 then 5 else -5 end
    x.abs
    nil                             # nil是一个对象
    0.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 * -1
    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 42)
    end
    public :silly
    5.silly(5){ |b| b*2 }
    

    11 Procs

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

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

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

    12 Hashes和Ranges

    哈希表{}

    h = {"SML" => 7, "Racket" => 12, "Ruby" => 42}
    puts h
    h2 = {:sml => 7, :racket => 12, :ruby => 42}
    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 < 50 }
    end
    
    puts foo [3,5,7,9]
    puts foo (3..9)
    
    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 = 0 then true else odd (x - 1)
    and odd x = if x = 0 then false else even (x - 1)
    
    (* 不会修改odd的行为,因为odd在定义的环境中查找even *)
    fun even x = false
    
    (* 用一个更优化的版本替换even,odd是无法获得这个优化实现的好处 *)
    fun even x = (x mod 2) = 0
    

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

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

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

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

    作者: ntestoc

    Created: 2019-01-09 三 13:25

  • 相关阅读:
    haproxy教程
    haproxy和keepalived的理解(转载)
    redis集群搭建_超详细
    磁盘IO过高时的参考
    tomcat优化
    MYSQL数据库的主从复制
    k8s学习笔记-etcd介绍和集群搭建
    python排序算法二---冒泡排序
    Python排序算法一—快速排序
    python:如何判断字典a在字典b
  • 原文地址:https://www.cnblogs.com/ntestoc/p/10201595.html
Copyright © 2011-2022 走看看