曾经以为[each == 迭代(Iteration)
, map == 枚举(enumerate)
],後来发现错得非常离谱。
有请:
教育部重编国语辞典修订本
交换替代。《文选.张衡.东京赋》:「於是春秋改节,四时迭代。」北周.庾信〈哀江南赋序〉:「呜呼!山岳崩颓,既履危亡之运,春秋迭代,必有去故之悲,天意人事,可以凄怆伤心者矣!」
一个接替一个,做重复的事。与回圈感觉上很像?
再有请:
自由的百科全书维基百科
回圈是计算机科学运算领域的用语,也是一种常见的控制流程。回圈是一段在程序中只出现一次,但可能会连续执行多次的程序码。回圈中的程序码会执行特定的次数,或者是执行到特定条件成立时结束回圈,或者是针对某一集合中的所有项目都执行一次。
迭代是指一个接着一个交换做,回圈是指重复执行。
会用到each与map的都是"集合资料"类别,例如Array与Hash。
2.7.3 :008 > [1, 2, 3].each
=> #<Enumerator: [1, 2, 3]:each>
2.7.3 :009 > {:a => 1, :b => 2, :c => 2}.each
=> #<Enumerator: {:a=>1, :b=>2, :c=>2}:each>
2.7.3 :010 > (1..5).each
=> #<Enumerator: 1..5:each>
2.7.3 :011 > 5.times.each
=> #<Enumerator: 5:times>
2.7.3 :012 > [1, 2, 3].map
=> #<Enumerator: [1, 2, 3]:map>
#map大同小异。
#Enumerator枚举器。
配合昨天说的block。
2.7.3 :013 > [1, 2, 3].each {|num| puts num + 1}
2
3
4
=> [1, 2, 3]
2.7.3 :014 > [1, 2, 3].map {|num| num + 1}
=> [2, 3, 4]
#一个一个元素进去操作,这件事情就是迭代。
可以看到有说明each、map为迭代器的资料,但它们是不是枚举器?
教育部重编国语辞典修订本。
一一列举。如:「随着科技进步,全世界每日新发明的产品,真是不胜枚举。」金.元好问〈故物谱〉:「住在乡里,常侍诸父及两兄燕谈,每及家所有书,则必枚举而问之。」元.陶宗仪《南村辍耕录.卷一九.阑驾上书》:「歌曰:『官吏黑漆皮灯笼,奉使来时添一重。』如此怨谣,未能枚举,皆万姓不平之气,郁结于怀,而发诸声者然也。」
在block内做的事情,就是一一列举。
2.7.3 :008 > [1, 2, 3].each
=> #<Enumerator: [1, 2, 3]:each>
2.7.3 :013 > [1, 2, 3].each {|num| puts num + 1}
2
3
4
=> [1, 2, 3]
When a block given, passes each successive array element to the block。-- form Array
Calls the given block with each key-value pair。-- form Hash
集合资料类别使用each与map,会产生枚举器Enumerator:
,接上black後会有迭代的行为,送进block後进行枚举
2.7.3 :001 > Enumerator.is_a? Class
=> true
2.7.3 :012 > new_enume = Enumerator.new {}
=> #<Enumerator: #<Enumerator::Generator:0x00007ff6ad33e900>:each>
是的,有这个类别,在一开始我是真的吓到,枚举也包装成类别了,但後来越了解Ruby里面都是物件这件事後,就觉得大惊小怪了,难怪枚举这麽强大。
这是Ruby API示范的费波......放弃,Fibonacci系数。
fib = Enumerator.new do |y|
a = b = 1
loop do
y << a
a, b = b, a + b
end
end
fib.take(10)
说明一下
"<<",感觉上跟很眼熟。
2.7.3 :009 > [] << 2
=> [2]
2.7.3 :010 > [] << [2]
=> [[2]]
还记得前面说过,Ruby有很多相同名称方法,在Enumerator类别中,变数使用<<
成为yielder
的对象来取得值,可以当成方法的别名,而其实在Array中,<<
是push
方法的别名。但行为上都很像赋予值对吧,更感觉到duck typing
的好处了。
会有别名方法的简单介绍的....如果时间够。
冷知识,map是collect的别名,为了跳槽的魔法师们设计,当然是语法里有map的魔法师们。
继续回到官网示范。
fib = Enumerator.new do |y|
a = b = 1
loop do
y << a # 循环中y被赋予a值,每个变数被赋予a的值。
a, b = b, a + b # 但a与b的值,会依造此规则变化。
end
end
2.7.3 :010 > fib.take(10) # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
=> [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
还可以这样玩。
2.7.3 :042 > fib = Enumerator.new do |y|
2.7.3 :043 > i = 0
2.7.3 :044 > a, b = 2, 3
2.7.3 :045 > loop do
2.7.3 :046 > y << a
2.7.3 :047 > a, b = b, a+b
2.7.3 :048 > i += 1
2.7.3 :049 > puts "某题:#{i}年後,动物有#{a}只的问题"
2.7.3 :050 > end
2.7.3 :051 > end
=> #<Enumerator: #<Enumerator::Generator:0x00007f9b6e469fb8>:each>
2.7.3 :052 > fib.take(10)
某题:1年後,动物有3只的问题
某题:2年後,动物有5只的问题
某题:3年後,动物有8只的问题
某题:4年後,动物有13只的问题
某题:5年後,动物有21只的问题
某题:6年後,动物有34只的问题
某题:7年後,动物有55只的问题
某题:8年後,动物有89只的问题
某题:9年後,动物有144只的问题
=> [2, 3, 5, 8, 13, 21, 34, 55, 89, 144]
虽然明明也是跟定义方法很像,但怎麽感觉这样处理,比较帅?
Enumerator
类别博大精深,它还有子系列是Enumerator::
,有兴趣可以先看看Enumerator::Lazy
,简单一点的说法是lazy
这个子系列避免枚举元素过多,属於优化的一种,Rails
内也会有所谓的Lazy load
喔。
高阶魔法....
Ruby API https://ruby-doc.org/core-3.0.2/Enumerable.html
Public Instance Methods
手册上可以看到有被标注Enumerable
的方法们,说是Ruby的精髓可能太夸张,但可以说是处理各种资料的专属Ruby利器,例如昨日提到的sort_by
。
以下是骗篇幅用的整理资料,不讨论用法,可以自行google翻译看看,看到中文看会不会顺利想到正确用法,可以更发现Ruby有多口语化。
`all?`、`any?`、`chain`、`chunk`、`chunk_while`、`collect`、`collect_concat`、`count`、`cycle`、`deect`、`drop`、`drop_while`、`each_cons`、`each_entry`、`each_slice`、`each_with_index`、`each_with_object`、`entries`、`filter`、`filter_map`、`find`、`find_all`、`find_index`、`first`、 `flat_map`、`grep`、`grep_v`、`group_by`、`include?`、`inject`、`lazy`、`map`、`max`、`max_by`、`member?`、`min`、`min_by`、`minmax`、`minmax_by`、`none?`、`one?`、`partition`、`reduce`、`reject`、`reverse_each`、`select`、`slice_after`、`slice_before`、`slice_when`、`sort`、`sort_by`、`sum`、`take`、`take_while`、`tally`、`to_a`、`to_h`、`uniq`、`zip`
用时没发现,回头整理才发现,原来用过的很多方法,都是有枚举这个行为存在。
2.7.3 :054 > ruby_chain = [1, 2].chain(3..9)
=> #<Enumerator::Chain: [[1, 2], 3..9]>
2.7.3 :056 > ruby_chain.to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
当初还以为这方法只是简单连接而已,结果也可以枚举。
2.7.3 :090 > arr = %w(恶魔灵魂 黑暗灵魂 黑暗灵魂2 血源诅咒 黑暗灵魂3)
=> ["恶魔灵魂", "黑暗灵魂", "黑暗灵魂2", "血源诅咒", "黑暗灵魂3"]
2.7.3 :091 > list = arr.to_enum
=> #<Enumerator: ["恶魔灵魂", "黑暗灵魂", "黑暗灵魂2", "血源诅咒", "黑暗灵魂3"]:each>
2.7.3 :092 > list.next
=> "恶魔灵魂"
2.7.3 :093 > list.next
=> "黑暗灵魂"
2.7.3 :094 > list.next
=> "黑暗灵魂2"
2.7.3 :095 > list.next
=> "血源诅咒"
2.7.3 :096 > list.next
=> "黑暗灵魂3"
2.7.3 :097 > list.next
StopIteration (iteration reached an end)
可以利用next
方法进行分次枚举,也是利用到昨天的yield
原理。
一样看程序码比较有感觉。
2.7.3 :098 > games = Object.new
=> #<Object:0x00007f9b6e3f07a8>
2.7.3 :099 > def games.each
2.7.3 :100 > p "恶魔灵魂先出在PS3"
2.7.3 :101 > yield
2.7.3 :102 > p "黑暗灵魂也出在PS3"
2.7.3 :103 > yield
2.7.3 :104 > p "黑暗灵魂2也先出在PS3"
2.7.3 :105 > yield
2.7.3 :106 > p "血源诅咒出在PS4"
2.7.3 :107 > yield
2.7.3 :108 > p "黑暗灵魂3出在PS4"
2.7.3 :109 > yield
2.7.3 :110 > p "下一款叫做Elden Ring"
2.7.3 :111 > end
=> :each
2.7.3 :112 > test = games.to_enum
=> #<Enumerator: #<Object:0x00007f9b6e3f07a8>:each>
2.7.3 :113 > test.next
"恶魔灵魂先出在PS3"
=> nil
2.7.3 :114 > test.next
"黑暗灵魂也出在PS3"
=> nil
2.7.3 :115 > test.next
"黑暗灵魂2也先出在PS3"
=> nil
2.7.3 :116 > test.next
"血源诅咒出在PS4"
=> nil
2.7.3 :117 > test.next
"黑暗灵魂3出在PS4"
=> nil
2.7.3 :118 > test.next
"下一款叫做Elden Ring"
StopIteration (iteration reached an end)
看来是很想玩game了
第五天的Leetcode350:Intersection of Two Arrays II
但是先看一下它的前一题Leetcode349:Intersection of Two Arrays
349的题目连结:https://leetcode.com/problems/intersection-of-two-arrays/
题目重点:取两个阵列交集的元素,必须uniq。
# @param {Integer[]} nums1
# @param {Integer[]} nums2
# @return {Integer[]}
def intersection(nums1, nums2)
end
有时间可以研究回圈怎麽跑,可以帮助思考怎麽枚举,没时间就用Ruby内建方法。
2.7.3 :001 > [2, 5, 6, 3, 8] & [1, 2, 4, 7 ,4]
=> [2]
所以答案当然是
def intersection(nums1, nums2)
nums1 & nums2
end
可以於Array类别内看到&
这方法。
&
的原理。
nums1.uniq - ( nums1.uniq - nums2.uniq )
2.7.3 :122 > [1, 2, 4, 6, 7].intersection([4, 6])
=> [4, 6]
2.7.3 :123 > [1, 2, 4, 6, 7].intersection([4, 6], [1,7])
=> []
2.7.3 :124 > [1, 2, 4, 6, 7].intersection([1, 4, 6], [1, 4, 7])
=> [1, 4]
350的题目连结:https://leetcode.com/problems/intersection-of-two-arrays-ii/
题目重点:交集数不再是uniq,例如都有两个2,就是要出现2次。
# @param {Integer[]} nums1
# @param {Integer[]} nums2
# @return {Integer[]}
def intersect(nums1, nums2)
end
逻辑大概如下
$ #指针又出来了
[4, 9, 5] #arr1
[9, 4, 9, 8, 4] #arr2
{} #准备一个记录器
#开跑
$ #下面的阵列,我有一个4,你有没有4?
[4, 9, 5]
[9, 4, 9, 8, 4].count(4) #=>2 有,2个。
{4 => {arr1: 1, arr2:2}} #纪录下来
#继续
$ #下面的阵列,我一个9,你?
[4, 9, 5]
[9, 4, 9, 8, 4].count(9) #=>2 有,2个
{4 => {arr1: 1, arr2:2}, 9 => {arr1: 1, arr2: 2}} #纪录下来
#继续
$ #下面的阵列,我一个5,你?
[4, 9, 5]
[9, 4, 9, 8, 4].count(5) #=>0 ,没有。
{4 => {arr1: 1, arr2:2}, 9 => {arr1: 1, arr2: 2}} #没有就不必纪录。
#不用继续了,跑完了,即使arr2有不同元素,也成为不了交集。
#分析将
{4 => {arr1: 1, arr2:2}, 9 => {arr1: 1, arr2: 2}}
#成为一个阵列
4是交集,arr2出现2次,arr1出现一次,依照规则是取小的。
所以像是
2.7.3 :125 > x = [2 , 1].min
=> 1
2.7.3 :129 > [].push(4)*x
=> [4]
#9以此类推。
349让我们了解交集这件事,那我们就省去了检测这件事了。
def intersect(nums1, nums2)
arr = []
(nums1 & nums2).each do |num|
arr += Array.new([nums1.count(num), nums2.count(num)].min , num)
end
arr
end
#原理
2.7.3 :130 > [2, 1].min
=> 1
2.7.3 :131 > Array.new(5, 1)
=> [1, 1, 1, 1, 1]
#提醒下面写法不行
2.7.3 :144 > [] += Array.new(4) #喷错
2.7.3 :142 > arr = []
=> []
2.7.3 :143 > arr += arr << 4
=> [4, 4] #资料不正确。
记得回传arr
用map也可以,一样还是要回传arr,所以我用each。
今日有提到的
1.迭代、 枚举
中间提到的动物问题
真的是考古题喔!
2.Leetcode350:Intersection of Two Arrays II
明日利用&
做一个很阳春的名字检验器
,以及分享几题考古题。
>>: @Day5 | C# WixToolset + WPF 帅到不行的安装包 [变更UI预设介面]
参考文章:Element UI 上传档案 el-upload —— 手动上传档案,限制上传档案数量,...
由於今天比较晚才到家,所以可能内容会比较少一点。 另外今天在火车上看到github上面有几个开源的 ...
今天会来说明 TypeScript 中内建 ReturnType 和 Parameters 的原始...
观看Laravel 8.0的官方文件教学,可以看到一个新的东西就是我们这次要介绍的Sail,用起来非...
分散式运算架构 MapReduce 利用函式语言程序设计的概念,将分散运算分为映射(Map)和归纳...