美文网首页
Day6 读书笔记&心得体会

Day6 读书笔记&心得体会

作者: 柳辉 | 来源:发表于2017-06-01 11:26 被阅读14次

    一、读书笔记
    3.4 剩余部分
    当对象需要访问同类的其他对象的内部状态时,使用保护访问(protected access)方式。例如,我们希望单个的Account对象能够比较它们的原始余额,而对其余所有对象隐藏这些余额(可能因为我们要以一种不同的形式表现它们)。

    class Account
      attr_reader :cleared_balance
      protected :cleared_balance
      def greater_balance_than(other)
        return @cleared_balance > other.cleared_balance
      end
    end
    

    因为属性balance是protected,只有Account的对象才可以访问它。

    3.5 变量

    现在我们已经辗转创建了所有这些对象,让我们确保没有丢掉它们,变量用来保存这些对象的印迹;每个变量保存一个对象的引用。
    让我们通过下面的代码来验证。

    person = "Tim"
    person.object_id # 70176096289900
    person.class # String
    person # "Tim"
    

    第一行代码,Ruby使用值“Tim”创建了一个String对象,这个对象的一个引用(reference)被保存在局部变量person,接下来的快速检查展示了这个变量具备字符串的特性,它具有对象的ID、类和值。

    那么,变量是一个对象吗?在Ruby,答案是“不”,变量只是对象的引用,对象漂浮在某处一个很大的池中(大多数时候是堆,即heap中),并由变量指向它们。

    让我们看一下稍复杂的例子:

    person1 = "Tim"
    person2 = person1
    person1[0] = "J"
    
    person1 -> "Jim"
    person2 -> "Jim"
    

    发生了什么?

    原来的person1是"Tim",然后我们更改了person1的第一个字母。结果person1和person2都从"Tim"变成了"Jim"。

    这都归结于变量保存的是对象引用,而非对象本身这一事实。将person1赋值给person2并不会创建任何新的对象;它只是将person1的对象引用赋值给person2,因此person1和person2都指向同一对象。

    复制别名(alias)对象,潜在地给了你引用同一对象的多个变量。但这不会在你的代码中导致问题?它会的,但是并不向你想象的频繁(例如Java中的对象,也以相同的方式运作)。例如,在插图3.1的例子中,你可以通过使用String的dup方法来避免创建别名,它会创建一个新的、具有相同内容的String对象。

    person1 = "Tim"
    person2 = person1.dup
    person1[0] = "J"
    person1 -> "Jim"
    person2 -> "Tim"
    

    你可以通过冻结一个对象来阻止其他人对其进行改动,试图更改一个被冻结的对象,Ruby将引发(raise)一个TypeError异常。

    person1 = "Tim"
    person2 = person1
    person1.freeze -> prevent modifications to the object
    person2[0] = "J"
    

    第四章 容器、Blocks和迭代器

    只有一首歌曲的点唱机是不可能受欢迎的,所以我们应该开始考虑建立一个歌曲目录和待播放歌曲列表。
    它们都是容器(containers),所谓容器是指含有一个或多个对象引用的对象。

    目录和播放列表需要一组相似的方法:添加一首歌曲,删除一首歌曲,返回歌曲列表等等。播放列表可能还需要执行额外的任务,例如偶尔插播广告或者记录累计的播放时间,不过我们在后面才考虑这些问题,现在看来,开发一个通用的SongList类,然后将其特化(specialize)为目录和播放列表类,似乎是个好主意。

    4.1 容器(containers)

    开始实现之前,我们需要决定如歌在SongList对象中存储歌曲列表。目前有3个明显的选择:(1)使用Ruby的Array(数组);(2)使用Ruby的Hash(散列表);(3)自定义列表结构。

    4.1.1 数组

    数组类含有一组对象引用,每个对象引用占数组中的一个位置,并由一个非负的整数索引来标识。

    可以通过使用字面量(literal),或显式地创建Array对象,来创建数组,字面量数组(literal array)只不过是处于方括号中的一组对象。

    b = Array.new
    b.lenght -> 0
    b.class -> array
    b[0] = "second"
    

    数组由[]操作符来进行索引,和Ruby的大多数操作符一样,它实际上是一个方法(Array类的一个实例方法),因此可以被子类重载,如上面例子所示,数组的下标从0开始,使用非负整数访问数组,将会返回出于该整数位置上的对象,如果此位置上没有对象,则返回nil,使用负整数访问数组,则从数组末端开始计数。

    a = [1,2,3]
    a[-1] -> 3
    a[-2] -> 2
    a[-99] -> nil
    

    你也可以使用一对数字[start, count]来访问数组,这将返回一个包含从start开始的count个对象引用的新数组。

    a = [1, 2, 3]
    a[0..2]  -> [1,2,3]
    

    最后,你还可以使用range来对数组进行索引,其开始和结束位置被两个或者3个点分隔开,两个点的形式包含结束位置,而3个点的形式不包含。

    a = [1, 2, 3]
    a[1] = 'bag' -> [1, "bag", 2, 3]
    a[5] = 66 -> [1, "bag", 2, 3, nil, 66]
    

    数组还有大量的其他有用的方法,使用这些方法,你可以用数组来实现栈(stack)、收集(set)、队列(queue)、双向队列(dequeue)和先进先出队列(fifo)。

    4.1.2 散列表
    Hashed(也称关联数组、图或词典)和数组的相似之处在于他们都是被索引的对象引用集合,不过数组只能用整数来进行索引,而hash可以用任何类型的对象来进行索引,比如字符串、正则表达式等等。当你将一个值存入hash是,其实需要提供两个对象,一个索引(key),另一个值。随后,你可以通过键去索引hash以获得其对应的值,hash中的值可以是任意类型的对象。

    下面的例子使用了hash字母符表示法:处于花括号之间的key =>value配对的列表。

    h = { 'dog' => 'canie', 'cat' => 'feline', 'donkey' => 'asinine' }
    h.length ->3
    h['dog'] -> "canine"
    h['cow'] = "bob dy"
    

    和数组相比,hashes有一个突出的优点:可以用任何对象做索引,然而它也有一个突出的去缺点:它的元素是无序的,因此很难使用hash来实现栈和队列。

    你会发现hash是ruby最常用的数据结构之一。

    4.1.3 实现一个SongList容器

    在简单介绍了数组和hash后,该来实现点唱机的SongList了,让我们来设计一组SongList类所需要的基本方法,之后我们逐步扩充,但现在这些已经足够了。

    添加给定的歌曲列表中。

    delete_first() -> song

    删除列表的第一首歌曲,并返回该歌曲。

    delete_last -> song

    删除列表的最后一首歌曲,并返回该歌曲。

    [index] -> song

    返回指定名字的歌曲

    这个列表为如何实现SongList给出了提示。既能在尾部添加歌曲,又能在头部删除歌曲,这提示我们使用双向队列(即有两个头部的队列)可以使用Array来实现它。

    同样,数组也支持返回列表中整数位置歌曲。

    然而我们也需要使用歌曲名来检索歌曲,这可以通过以歌曲为键、歌曲为值的hash来实现。我们可以用hash吗?也许可以,但是我们会发现,这样会有问题。

    首先,hash是无序,所以我们可能需要一个辅助数组来跟踪歌曲列表。第二,hash不支持多个键对应一个相同的值,这对实现播放列表不利,因为播放列表可能需要播放同一首歌多次,因此目前我们先使用数组来实现,并在需要的时候搜索歌名,如果这成为瓶颈,我们可以添加一些基于hash的搜索功能。

    SongList类的实现以一个基本的initialize方法开始,该方法会创建容纳歌曲的数组,并将该数组的引用存储到实例变量@songs中。

    Class SongList
      def initialize
        @songs = Array.new
      end
    end
    

    SongList#append方法将给定的歌曲添加到@songs数组的尾部,它会返回self,即当前SongList对象的引用。这是一本很有用的惯用法,可以让我们把对append的对个调用连接在一起。举个栗子:

    class SongList
     def append(song)
       @songs.push(song)
     end
    end
    

    接着我们来添加delete_first和delete_last方法,它们分别用Array#shift和Array#pop来实现。

    class SongList
      def delete_first
        @songs.shift
      end
      def delete_last
        @songs.pop
      end
    end
    

    到目前为止,都还不错哦,下一个要实现的方法是[],用它通过下标来访问元素。这种简单的代理形式的方法在Ruby代码中经常可见:如果你的代码含有大量一两行的方法,不用担心——那是你设计正确的迹象。

    class SongList
      def [](index)
        @songs[index]
      end
    end
    

    现在做个简单的测试,我们将使用Ruby标准发行版自带的一个称谓TestUnit的测试框架来做这个工作。但是不会在这里详细介绍它。assert_equal方法检查它的两个参数是否相等,如果不等则立即报错。同样,assert_nil方法在它的参数不为nil时也会报错,我们将会使用这些来保证从列表中删除适当的歌曲。

    测试需要必要的初始化,以告诉RUby使用TestUnit测试框架,并告诉该框架我们在写一些测试代码。然后创建一个SongList对象和4首歌曲对象,并添加歌曲到列表中(趁机炫耀一下,我们利用了“append”返回SongList对象)这一特点来连接方法调用,

    接着,我们测试[]方法,验证它是否返回了指定下标处的正确的歌曲(或者nil)。最后,我们从列表的首位删除歌曲,并验证返回正确的歌曲。

    require 'test/unit'
    
    class TestSongList < Test::Unit::TestCase
      def test_delete
        list = SongList.new
        s1 = Song.new("title1", "artist1", 1)
        s2 = Song.new("title2", "artist2", 2)
        s3 = Song.new("title3", "artist3", 3)
        s4 = Song.new("title4", "artist4", 4)
    
        list.append(s1).append(s2).append(s3).append(s4)
    
        assert_equal(s1, list[0])
        assert_equal(s3, list[2])
        assert_nil(list[9])
    
        assert_equal(s1, list.delete_first)
        assert_equal(s2, list.delete_first)
        assert_equal(s4, list.delete_last)
        assert_equal(s3, list.delete_last)
        assert_nil(list, delete_last)
      end
    end
    

    是时候添加搜索功能了,这要求遍历列表中的所有歌曲,并检查每首歌的名字,为了实现这项功能,我们先说一下:## 迭代器

    4.2 Blocks 和 迭代器

    实现SongList的下一个问题是实现with_title方法,该方法接受一个字符串参数,并返回以此为歌名的歌曲,有个直接实现的方法:因为我们有歌曲数组,所以可以遍历该数组的所有元素,并查找出匹配的元素。

    class SongList
      def with_title(title)
        for i in 0...@songs.length
          return @songs[i] if title == @songs[i].name
        end
        return nil
      end
    end
    

    这个方法确实可行,而且也相当常见:用for循环遍历数组,但是有没有更自然的方式呢?

    的确有,在某种程度上,for循环和数组耦合过于紧密:需要知道数组的长度,然后依次获得其元素的值,直到找到一个匹配为止,为什么不只是请求数组对它的每一个元素执行一个测试呢?这正是数组的find方法要做的事情。

    class SongList
      def with_title(title)
        @songs.find{|song| title == song.name}
      end
    end
    

    find方法是一种迭代器,它反复调用block中的代码。迭代器和block是Ruby最有趣的特性之一,所以我们花一点时间来了解它们。

    4.2.1 实现迭代器(implementing lterators)

    Ruby的迭代器只不过是可以调用block的方法而已,咋看之下,Ruby的代码区块和C、Java、C#、Perl的代码区块很相似,实际上这有点蒙蔽人——Ruby的block不是传统的意义上的、将语句组织在一起的一种方式。

    首先,block在代码中只和方法调用一起出现;block和方法调用的最后一个参数处于同一行,并紧跟在其后(或者参数列表的右括号的后面)。其次,在遇到block的时候,并不立刻执行其中的代码。Ruby会记住block出现时的上下文(局部变量、当前对象等)然后执行方法调用。

    在方法内部,block可以像方法一样被yield语句调用,每执行一次yield,就会调用block中的代码,当block执行结束时,控制返回到紧随yield之后的那条语句。我们来看个简单的例子。

    def three_times
      yield
      yield
      yield
    end
    three_times { puts "Hello" }
    

    block(花括号内的代码)和对方法three_times的调用联合在一起。该方法内部,连续3次调用了yield。每次调用时,都会执行block中的代码,并且打印出一条欢迎信息。更有趣的是,你可以传递参数给block,并获得返回值,例如,我们可以写个简单的函数返回低于某个值的所有Fibonacci数列项。

    def fib_up_to(max)
      i1, i2 = 1, 1 # 并行赋值(i1 = 1 and i2 = 1)
      while i1 <= max
        yield i1
        i1, i2 = i2, i1+i2
      end
    end
    
    fib_up_to(1000) { |f| print f, " " }
    

    输出结果:

    1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
    

    注意:程序员迷们会很乐意看到yield关键字,它模仿了Liskov的CLU语言中的yield函数,CLU是一个超过20年之久的语言,其包含的特性还未被完全开发出来。
    基本Fibonnacci数列是以两个1开头的整数序列,其中的每一项都是它的前两项的和,它常被用在排序算法和自然现象分析中。

    在这个例子中,yield语句带有一个参数,参数值将被传送给相关的block。在block定义中,参数列表位于两个竖线(管道符)之间,在这个例子中,变量f收到yield的参数的值,所以block能够输出数列中的下一个项(这个例子也展示了并行赋值的用法)。尽管通常block只有一个参数,但这不是必然的,block可以有任意数量的参数。

    如果传递给block的参数是已存在的局部变量,那么这些变量即为block参数,它们的值可能会因为block的执行而改变,同样的规则适用于block内的变量:如果它们第一次出现在block内,那么它们就是block的局部变量。相反,如果它们先出现在block外,那么block就与外部环境共享这些变量。

    在下面这个(认为设计)的例子中,我们看到了从外部环境中继承了变量a和b,而c是block的局部变量(defined?方法在其参数没有定义时返回nil)。

    a = [1, 2]
    b = 'cat'
    a.each { |b| c = b * a[1] }
    a -> [1, 2]
    

    block 也可以返回值给方法,block内执行的最后一条表达式的值被作为yield的值返回给方法,这也是Array类的find方法的工作方式。它的实现类似于下面的代码。

    class Array
      def find
        for i in 0...size
          value = self[i]
          return value if yield(value)
        end
        return nil
      end
    end
    

    输出结果:

    [1, 3, 5, 7, 9].find { |v| v*v > 30 }
    

    上面的代码把数组的元素依次传递给关联的block。如果block返回真,那么方法返回相应的元素,如果没有元素匹配,方法返回nil,这个例子展示了这种迭代器的方式的优点,数组类处理它擅长的事情,例如访问数组元素,而让应用程序代码集中精力处理特殊需求(本例的特殊需求是找到满足某些算术标准的数组项)。

    一些迭代器是Ruby的许多收集(collection)类型所共有的。我们已经看了find方法,另外两个是each和collect。each可能是最简单的迭代器,它所做的就是连续访问收集的所有元素。

    [ 1, 3, 5, 7, 9 ].each { |i| puts i }
    

    输出结果:

    1
    3
    5
    7
    9
    

    echo迭代器在Ruby中有独特的作用,另一个常用的迭代器是collect,它从收集中获得各个元素并传递给block。block返回的结果被用来生成一个新的数组,例如:

    ["H", "A", "L"].collect { |x| x.succ } -> ["I", "B", "M"]
    

    迭代器并不仅局限于访问数组和hash中的已有数据,从Fibonacci的例子中,我们已经看到迭代器可以返回得到的值。这个功能被RUby的输入/输出类所使用,这些类实现了一个迭代器接口以返回得到的值。这个功能被Ruby的输入/输出类所使用,这些类实现了一个迭代器接口以返回I/OL流中的连续相继的行(或字节)。下面的例子使用了do...end来定义block。这种方式定义block和使用花括号定义block的唯一区别是优先级:do...end的绑定低于{...}。

    f = File.open("testfile")
    f.each do |line|
      puts line
    end
    f.close
    

    输出结果:

    This is line one
    This is line two
    This is line three
    And so on...
    

    让我们再看一个有用的迭代器。inject(名字有点难理解)方法(定义在Enumerable模块中)让你可以遍历收集的所有成员以累积出一个值。例如,使用下面的代码你可以将数组中的所有元素加起来,并获得它们的累加和。

    [1, 3, 5, 7].inject(0) { |sum, element| sum+element } 
    [1, 3, 5, 7].inject(1) { |product, element | product*element }
    

    inject是这样工作的:block第一次被执行时,sum被置为inject的参数,而element被置为收集的第一个元素。接下来的每次执行block时,sum被置为上次block被调用时的返回值。inject没有参数,那么它使用收集的第一个元素作为初始值,并从第二个元素开始迭代。这意味着我们可以把前面的例子写成:

      [1, 3, 5, 7].inject { |sum, element| sum+element }
      [1, 3, 5, 7].inject { |product, element| product*element }
    

    内迭代器和外迭代器

    Ruby实现迭代器的方式与其他如C++何Java等语言实现迭代器的方式,值得我们做一下比较。在Ruby中,迭代器集成于收集内部——它只不过是一个方法,和其他方法不同的是,每当产生新的值得时候调用yield。使用迭代器的不过是和该方法相关联的一个代码block而已。

    在其他语言中,收集本身没有迭代器,他们生成外部辅助对象(例如Java中基于Interator接口的对象)来传送迭代器状态。从这点看来(当然还可从很多方面来看),Ruby是一种透明的语言。你在写程序的时候,Ruby语言能使你集中精力在你的工作上,而不是语言本身上。

    我们值得花点时间看看为什么Ruby的内部迭代器并不总是最好的解决方案。当你需要把迭代器本身作为一个对象时(例如,将迭代器传递给一个方法,而该方法需要访问由迭代器返回一个值),它的表现欠佳了。另外,使用RUby内建的迭代器模式也难以实现并行迭代两个收集。幸运的是,Ruby提供了Generator库,该库为解决这些问题实现了外部迭代器。

    4.2.2 事物 Blocks
    尽管block通常和迭代器合用,但它还有其他用处。我们来看其中几个用法。
    block可以用来定义必须运行在事务控制环境下的代码。比如,你经常需要打开一个文件,对其内容做些处理,然后确保在处理结束后关闭文件。尽管可以用传统的方式实现,但也存在“应该由文件负责自身的关闭”这样的观点。我们可以用block来实现这种需求。如下是一个简单且忽略了错误处理的例子:

    class File
      def File.open_and_process(*args)
        f = File.open(*args)
        yield f
        f.close()
      end
    end
    
    File.open_and_process("testfile", "r") do |file|
      while line = file.gets
        puts line
      end
    end
    

    输出结果:
    This is line one
    This is line two
    This line three
    And so on...

    open_and_process是一个类方法,它可以独立于任何file对象来被使用。我们希望它接受与传统的File.open一样的参数,但并不关心这些参数到底是什么?所以,我们用args表示参数,这意味着“把传递给这个方法的实际参数收集到名字为args的数组中。”然后我们调用File.open,并以args作为参数。它将把数组参数扩展成独立的参数,最终的结果是,open_and_process透明地将它所接收的任意参数都传递给了File.open。

    一旦文件被打开,open_and_process将调用yield,并传递打开的文件对象给block。当block返回时,文件即被关闭,通过这种方式,关闭打开文件的责任从文件使用者身上转移到了文件本身。

    让文件管理它自己的生命周期的技术如此重要,以至于Ruby的File类直接支持了这项技术,如果File.open有个关联的block,那么该block将被调用,且参数是该文件对象。当block执行结束时,文件会被关闭,这非常有趣,因为它意味着File.open有两种不同的行为:当和block一起调用时,它会执行该block并关闭文件;

    当单独调用时,它会返回文件对象,使得这种行为成为可能的是,Kernel.block_given?方法,当某方法和block关联在一起调用时,Kernel.block_given?将返回真。使用该方法,可以用下面的代码(同样也忽略了错误处理)实现类似于标准的File.open方法。

    class File
      def File.my_open(*args)
        result = file = File.new(*args)
    
        if block_given?
          result = yield file
          file.close
        end
    
        return result
      end
    end
    

    还有一点不足:前面的例子在使用block来控制资源时,我们还没有解决错误处理问题,如果想完整实现这些方法,那么即使处理文件的代码由于某种原因异常中断,我们也需要确保文件被关闭。后面谈到的异常处理可以解决这个问题。

    4.2.3 Blocks可以作为闭包

    让我们再回到点唱机上(还记得点唱机的例子吧)。在某些时候,我们需要处理用户界面——用户用来选择歌曲和控制点唱机的按钮。我们需要将行为关联到这些按钮上:当按“开始”按钮时,开始播放音乐。事实证明,Ruby语言的block是实现这种需求的合适方式,假设点唱机的硬件制造商实现了一个Ruby扩展,该扩展提供了一个基本的按钮类。

    start_button = Button.new("Start")
    pause_button = Button.new("Pause")
    

    当用户按其中一个按钮时会发生什么呢?硬件开发人员做了埋伏,使得按钮按下时,调用Button类的回调函数button_pressed。向按钮类中添加功能的一个显而易见的方式是穿件Button类的子类,并让每个子类实现自己的button_pressed方法。

    class StartButton < Button
      def initialize
        super("Start")
      end
      def button_pressed
        # do start actions
      end
    end
    
    start_button = StartButton.new
    

    这样做有两个问题,首先,这会导致大量的子类,如果Button类的接口发生变化,维护代价将会提高,其次,按下按钮引发的动作所处层次不当:它们不是按钮的功能,而是使用按钮的点唱机的功能。使用Block可以解决这些问题。

    songlist = SongList.new
    class JukeboxButton < Button
      def initialize(label, &action)
        super(label)
        @action = action
      end
    
      def button_pressed
        @action.call(self)
      end
    end
    
    start_button = JukeboxButton.new("Start") { songlist.start }
    pause_button = JukeboxButton.new("pause") { songlist.pause }
    

    上面代码的关键之处在于JukeboxButton#initialize的第二个参数,如果定义方法时在最后一个参数前加一个&(例如&action),那么当调用该方法时,Ruby会寻找一个block。block将会转化成Proc类的一个对象,并赋值给了实例变量@action。这样当回调函数button_pressed被调用时,我们可以Proc#call方法去调用相应的Block。

    但是,当我们创建Proc对象时,到底获得了什么?有趣的是,我们得到的不仅仅是一堆代码。和block(以及Proc对象)关联在一起的还有定义block时的上下文,即self的值、作用域内的方法、变量和常量。Ruby的神奇之处是,即使block被定义时的环境早已消失了,block仍然可以使用其原始作用域中的信息。在其他语言中,这种特性称之为闭包。

    让我们来看一个故意设计的例子,该例使用了lambda方法,该方法将一个block转换成了Proc对象。

    def n_times(thing)
      return lambda { |n| thing * n }
    end
    
    p1 = n_times(23)
    p1.call(3)
    p1.call(4)
    p2 = n_times("Hello ")
    p2.call(3)
    

    n_times方法返回引用了其参数thing的Proc对象。尽管block被调用时,这个参数已经出了其作用域,但是block仍然可以访问它。

    4.3 处处皆是容器

    容器、block和迭代器是Ruby的核心概念,用Ruby写的代码越多,你就会发现自己对传统循环结构使用的越少。你会更多的写支持迭代自身内容的类,而且你会发现这些代码精简并易于维护。

    第5章 标准类型

    到目前为止,我们已经对点唱机有了好玩的实现,但同时有所取舍,前面我们降到了数组、散列、proc。但还没有真正谈到Ruby中的其他一些基本类型:数字(number)、字符串、区间(range)和正则表达式。

    5.1 数字

    Ruby支持整数和浮点数。整数可以是任何长度(其最大值取决于系统可用内存的大小)。一定范围内的整数(通常是-230到230-1或-262到262-1)在内部以二进制形似存储,它们是Fixnum类的对象。这个范围之外的整数存储在Bignum类的对象中(目前实现为一个可变长度的短整型集合)。这个处理是透明的,Ruby会自动管理它们之间的来回转换。

    num = 81
    6.times do
      puts "#{num.class}: #{num}"
      num *=num
    end
    

    输出结果:

    Fixnum:81
    Fixnum:6561
    

    在书写整数时,你可以使用一个可选的前导符号,可选的进制指示符(0表示八进制, 0d表示十进制[默认]),0x表示十六进制或者0b表示二进制),后面跟一串符合适当进制的数字。下划线在数字串中被忽略(一些人在更大的数值中使用它们来代替逗号)。

    控制字符的整数值可以使用?\C-x和cx(x的control版本,是x&0x9f)生成,元字符(x | 0x80^2)可以使用?\M-x生成。元字符和控制字符的组合可以使用?\M-\C-x生成。可以使用?\序列得到反斜线字符的整数值。
    ?a => 97 # ASCII character
    ?\n => 10 # code for a newline (0x0a)
    ?\C-a => 1 # control a = ?A & 0x9f = 0x01
    ?\M-a => 225 #meta sets bit 7
    ?\m-\C-a =>129
    ?\C-? => 127

    与原声体系结构的double数据类型相对应,带有小数点和/或幂的数字字面量被转换成浮点对象。你必须在小数点之前和之后都给出数字(如果把1.0e3写成1.e3,Ruby会试图调用Fixnum类的e3方法)

    二、心得体会
    今天完成了什么?

    • 今天主要看了《PR》的4.1、4.2、4.3节
    • 看了auth目录下的代码

    今天的收获?

    其他收获

    • 今天听了得到订阅专栏《硅谷来信》里面提到,工作时要既看得见树木,也要看得见森林,意思就是既要把本职工作做好,也要考虑大局,不能只顾着自己的一亩三分地。
    • 同时,又讲到做什么事情前,先跟团队成员打个招呼总是好的。

    相关文章

      网友评论

          本文标题:Day6 读书笔记&心得体会

          本文链接:https://www.haomeiwen.com/subject/pqisfxtx.html