@smilence
2020-02-23T12:42:13.000000Z
字数 2878
阅读 825
Ruby
SDE-Foundations
本文将
Array
,String
,Hash
统称为collections 因为他们都由很多个基本类型的元素组成。
1.1 取Array和String的一部分
str = "abcd"
str[1..-1] # => 'bcd'
arr = [ele0, ele1, ele2, ele3]
arr[1..3] # => [ele1, ele2, ele3]
1.2 Tenary 用于正反两种情况下的不同值
value = condition ? x : y # value要么是 x 要么是 y
if ... else ... 也可以覆盖所有情况
而 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
do...end
中间,或者{...}
里面的一段代码。就是a block of code, that's it.return
关键词来代表,也可以用最后一行的表达式的值来代表。而block的结果只能用最后一行的表达式来代表.比如 do “hello" end
的结果是"hello"
。 do |ele| ele * 2 end
的结果自然和传入的ele
的值有关。注意,这个参数的scope限制在block内。Enumerators就是循环处理collection内元素的函数,他们通常都会take block作为参数。他们也可以同时接受普通参数, 比如 arr.inject(0) do ... end
。 Enumerator会把每一个元素feed给block,block每次只处理一个element。
3.1 arr.each
并不会对block的结果做什么处理,所以需要自己处理赋值等操作
# arr.each 负责把arr的每个元素传入block,这些block也有结果,但是arr.each并不会利用他们,所以你必须自己在block内做操作
arr.each do |ele|
new_arr << ele if ele.even?
end
3.2 其他的循环,会根据每一个元素通过block(就像是通过一个funnel)的结果来整合,比如变换 / 筛选 / 聚合
如果block内的内容也需要循环,推荐总是把block里的部分用一个新的函数来替代,这样函数的返回值一定是最后输出的结果。尤其是
array.select
这样需要boolean
结论的enumerator。 写法上一个简单的原则是,一个循环就对应一个新函数
# arr.map把每个元素通过block得到的结果依次放到new_array中并最后返回
new_arr = arr.map do |ele| # 对arr每个值变换产生一个新的值
... # 计算new_ele
new_ele # 输出新的ele (变换的结果) , 放到new_arr里
end
# arr.select 把每个元素通过block得到的结果作为筛选元素的标准,并把筛选后的array返回
new_arr = arr.select do |ele|
ele > 2 # 输出select的根据 (选哪些?)
end
# 把block的结果作为元素排序的根据,即 按照元素的什么排序?
new_arr = arr.sort_by {|ele| ele.length}
# 把每个元素通过block的结果作为判断标准,最后返回一个判断(boolean)
result = arr.all? { |ele| ele > 2 } # 同样适用于 arr.any? arr.none? arr.one? arr.count
# 把每个元素通过block的结果作为新的累计值,最后把最终的累计值作为arr.inject的返回值
result = arr.inject do |acc, ele|
new_acc = acc + ele # 对acc进行操作
new_acc # 输出新的acc
end
3.3 当我们只需要处理array的一部分,并且我们知道idx
的范围时,更适合用idx
的range来做each
4.1 函数计算完之后,最常见的与外面沟通的方式就是通过返回值 return
def doubler(num)
num = num * 2
return num
end
number = 1
number = doubler(number) # assign back to number
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,改一个就会改所有。
height # subarray的数量
width # subarray里元素的数量
2d_array = Array.new(height) { Array.new(width) } # or Array.new(height) {[]}
# there can never be assignment in Ruby without an = present
hash = Hash.new {|h,k| h[k] = []}