[关闭]
@smilence 2020-02-23T12:42:13.000000Z 字数 2878 阅读 825

Chapter 1: Ruby as a Computer Language

Ruby SDE-Foundations


本文将Array, String, Hash统称为collections 因为他们都由很多个基本类型的元素组成。

1. Basics

1.1 取Array和String的一部分

  1. str = "abcd"
  2. str[1..-1] # => 'bcd'
  3. arr = [ele0, ele1, ele2, ele3]
  4. arr[1..3] # => [ele1, ele2, ele3]

1.2 Tenary 用于正反两种情况下的不同值

  1. value = condition ? x : y # value要么是 x 要么是 y
  2. if ... else ... 也可以覆盖所有情况
  3. if 或者 if elsif 则未必,因为他们只注明了某些情况

1.3 *args_arr
*args_arr代表把传入的多个变量打包args_arr这个array

1.4 Block和Proc

Block 指具体的一个操作 {|ele| ele * 2 }, Proc 是指把这个操作变成一个变量: Proc.new {|ele| ele * 2}. &prc是指把传入的block赋值给prc这个 Proc。注意一个函数只能take一个block,但可以take很多Proc, 因为Proc和其他type并无本质差别。

Block的结果就是block最后一行的表达式 可以理解成block和函数一样也是有结果的,只不过以写在最后一行的形式而不是return来返回。

1.5 命名规范

用单数代表一个东西,用复数代表一个这种类型的array。比如words代表一个array,里面每个是word (string); procs代表一个array, 里面每个是Proc

1.6 需要比较或者交换前后元素的情况

对于需要比较或者交换前后元素的情况,用(0...arr.length-1)配合arr[idx]比直接用arr.each.with_index更好, 因为我们不需要每个元素ele

2. Block

  1. block只是do...end中间,或者{...}里面的一段代码。就是a block of code, that's it.
  2. 就像函数一样,block也是有结果的,只不过函数的结果可以用return关键词来代表,也可以用最后一行的表达式的值来代表。而block的结果只能用最后一行的表达式来代表.比如 do “hello" end的结果是"hello"
  3. block可以take参数,使得参数不同,block的结果也不同,比如 do |ele| ele * 2 end的结果自然和传入的ele的值有关。注意,这个参数的scope限制在block内。

3. Enumerators / 循环

Enumerators就是循环处理collection内元素的函数,他们通常都会take block作为参数。他们也可以同时接受普通参数, 比如 arr.inject(0) do ... end。 Enumerator会把每一个元素feed给block,block每次只处理一个element

3.1 arr.each并不会对block的结果做什么处理,所以需要自己处理赋值等操作

  1. # arr.each 负责把arr的每个元素传入block,这些block也有结果,但是arr.each并不会利用他们,所以你必须自己在block内做操作
  2. arr.each do |ele|
  3. new_arr << ele if ele.even?
  4. end

3.2 其他的循环,会根据每一个元素通过block(就像是通过一个funnel)的结果来整合,比如变换 / 筛选 / 聚合

如果block内的内容也需要循环,推荐总是把block里的部分用一个新的函数来替代,这样函数的返回值一定是最后输出的结果。尤其是 array.select 这样需要boolean结论的enumerator写法上一个简单的原则是,一个循环就对应一个新函数

  1. # arr.map把每个元素通过block得到的结果依次放到new_array中并最后返回
  2. new_arr = arr.map do |ele| # 对arr每个值变换产生一个新的值
  3. ... # 计算new_ele
  4. new_ele # 输出新的ele (变换的结果) , 放到new_arr里
  5. end
  1. # arr.select 把每个元素通过block得到的结果作为筛选元素的标准,并把筛选后的array返回
  2. new_arr = arr.select do |ele|
  3. ele > 2 # 输出select的根据 (选哪些?)
  4. end
  1. # 把block的结果作为元素排序的根据,即 按照元素的什么排序?
  2. new_arr = arr.sort_by {|ele| ele.length}
  1. # 把每个元素通过block的结果作为判断标准,最后返回一个判断(boolean)
  2. result = arr.all? { |ele| ele > 2 } # 同样适用于 arr.any? arr.none? arr.one? arr.count
  1. # 把每个元素通过block的结果作为新的累计值,最后把最终的累计值作为arr.inject的返回值
  2. result = arr.inject do |acc, ele|
  3. new_acc = acc + ele # 对acc进行操作
  4. new_acc # 输出新的acc
  5. end

3.3 当我们只需要处理array的一部分,并且我们知道idx的范围时,更适合用idx的range来做each

4. Reference (函数内外的沟通)

4.1 函数计算完之后,最常见的与外面沟通的方式就是通过返回值 return

  1. def doubler(num)
  2. num = num * 2
  3. return num
  4. end
  5. number = 1
  6. number = doubler(number) # assign back to number
  7. p number # => 2

4.2 Ruby的函数传的总是copy / value, 在函数里更改argument的值不会对函数外面的变量产生影响。 只不过对于collection,传的是reference的value,也就是一个地址的copy。所以虽然函数里没法改变collection的地址,但是可以通过这个地址来找到并修改“房屋”里的东西。也就是通过collection[i]来更改collection指向的那些元素。

4.3 对Collection in-place(原地)修改,必须要通过 collection[i] =或者collection[i] +=或者<<来修改或者map!reverse!之类的现成函数。 (对于Hash则是hash[key])

在新建一个Array或者Hash时, 如果每个元素是collection (比如Array of Arrays), 那么必须要通过block来初始化。否则每个元素都是同一个reference,改一个就会改所有。

  1. height # subarray的数量
  2. width # subarray里元素的数量
  3. 2d_array = Array.new(height) { Array.new(width) } # or Array.new(height) {[]}
  4. # there can never be assignment in Ruby without an = present
  5. hash = Hash.new {|h,k| h[k] = []}
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注