D-28.鸭子型别, 字串, 阵列, 范围, 杂凑

进入其他基本资料类别前。

想先说明以下观念。

2.7.3 :081 > 1 + 1
 => 2
# + 在这里是数字类别的方法

2.7.3 :082 > "1" + "1"
 => "11"
# + 在这里是字串类别的方法

1 + "1""1" + 1会喷TypeError错误,是因为两种类别的 + 方法,规定了所能运用的资料型态。而不是+方法自己规定了两种资料型态不能相加。

自己动手做一个同名但不同类别都有的方法。

class A_class
  def super_method
    puts "我是A方法"
  end
end
 => :super_method
 
class B_class
  def super_method
    puts "我是B方法"
  end
end
 => :super_method
 
2.7.3 :096 > a = A_class.new
 => #<A_class:0x00007fcb083dbcf8>
2.7.3 :097 > a.super_method
我是A方法
 => nil
2.7.3 :098 > b = B_class.new
 => #<B_class:0x00007fcb083aa978>
2.7.3 :099 > b.super_method
我是B方法
 => nil

为何提这个是因为,类别里常有"同名"的方法,在学习初期,我们常常会记Array也可以相加,但事实上是阵列也有自己的+的方法。

2.7.3 :100 > [1, 2, 3] + [1, 2, 3]
 => [1, 2, 3, 1, 2, 3]

duck typing

请不要把下面文章想成原理,想成是一个故事。

Ruby是鸭子型别的设计风格。

「当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那麽这只鸟就可以被称为鸭子。」

我们把一开始的同名方法整理一下。

class Dog
  def has_a_method
    puts "我会叫,我会跑,我会游泳"
  end
end

class Cat
  def has_a_method
    puts "我会叫,我会跑,我会游泳"
  end
end

class Duck_look_like
  def yes_i_can(another_animal)
    another_animal.has_a_method
  end
end

2.7.3 :252 > kind_of_animal = Duck_look_like.new
 => #<Duck_look_like:0x00007fa17b39dd10>

2.7.3 :253 > kitty = Dog.new
 => #<Dog:0x00007fa17b3265a8> #有一只kitty
 
2.7.3 :254 > lucky = Cat.new
 => #<Cat:0x00007fa17b417c78> #有一只lucky
#这边故意反惯例,表示不在意他们本质。

2.7.3 :255 > kind_of_animal.yes_i_can(kitty)
我会叫,我会跑,我会游泳
 => nil
2.7.3 :256 > kind_of_animal.yes_i_can(lucky)
我会叫,我会跑,我会游泳
 => nil

我在做的不是规定鸭子是什麽,而是让狗与猫变成像鸭子一样。

初学时会说,那这一切都是设计好的呀?

对,初期比起在意字串不是数字却能够相加,应该在意的是能让字串相加。

无论有人说这种设计让Ruby好学或是难学,好维护或难维护,但这种设计是为了我们能更快速操作"物件"。

关於duck typing还会有其他衍生的问题,例如Monkey Patch等,也是很好的讨论问题,这边稍微提到是因为明天会分享一些转换类型的leetcode题型做分享。

能解决问题就是好魔法。


字串

用"",与''包起来的任何字元都为字串。

2.7.3 :015 > "234234*(&%*(X34234.".class
 => String
2.7.3 :016 > '1+1=2'.class
 => String
2.7.3 :010 > "".class
 => String
2.7.3 :011 > ''.class
 => String
#即使""里面是nil,也是字串。

我们来让字串像其他类别吧。


2.7.3 :021 > "str" + "str"
 => "strstr"
2.7.3 :022 > "str"*8
 => "strstrstrstrstrstrstrstr"
 
2.7.3 :020 > str = String.new
 => ""
2.7.3 :023 > str << "123"
 => "123"
2.7.3 :024 > str << "456"
 => "123456"
2.7.3 :025 > "abc".bytes
 => [97, 98, 99]
#当然不只这些。

分享过的:leetcode.028:Implement strStr()
解法为利用index方法,由於已写过就不重复拿来骗篇幅,直接整理如下。

def str_str(haystack, needle)
  ans = haystack.index(needle)
  ans != nil ? ans : -1
end

def str_str(haystack, needle)
  haystack.index(needle) != nil ? haystack.index(needle) : -1
end

#看久了三元运算子很可爱,不常看觉得很讨厌,跟不准家里养宠物的爸妈,看到偷抱回来的毛小孩一样。

阵列

阵列由於记忆体储存方式,常会是用到回圈,迭代,枚举的好题型。

2.7.3 :026 > arr = Array.new
 => []
2.7.3 :027 > arr << 1
 => [1]
2.7.3 :028 > arr << 2
 => [1, 2]
2.7.3 :029 > arr << "3"
 => [1, 2, "3"]
# << 这个方法,阵列与字串的非常的不同。
#另外阵列也可以运用到运算符号,但使用结果与使用对象须注意
2.7.3 :031 > [1, 2, "3", 1, 2, "3"] - [1, 2, "3"]
 => []
2.7.3 :032 > [1, 1, 1] + [1, 2, 3]
 => [1, 1, 1, 1, 2, 3]
2.7.3 :033 > [1] * [1]
TypeError (no implicit conversion of Array into Integer)
2.7.3 :034 > [1] * 5
 => [1, 1, 1, 1, 1]

leetcode035:Search Insert Position
跟上题连结一样。

#先不简化
def search_insert(nums, target)
  nums.each_with_index do |num, index|  
    if num >= target 
      return index 
    end  #大於0的目标与里面的值的关系是,刚好相等时回报位置,比较小时取代里面值的位置
  end
  nums.size
end

def search_insert(nums, target)
  nums.each_with_index do |num, index|
    return index if num >= target
  end
  nums.size
end

范围

Range用到new时,需要带入正确参数,所以在解题上常会直接写出自己要的范围,不常会用到new。

2.7.3 :036 > Range.new
ArgumentError (wrong number of arguments (given 0, expected 2..3))
#也不是随便带参数就可以。
2.7.3 :045 > Range.new(1, 6)
 => 1..6
2.7.3 :046 > Range.new("a", 6)
ArgumentError (bad value for range)
2.7.3 :047 > Range.new("a", "b")
 => "a".."b"

#字串可以的原因,字母本身具有自己的字节数。
2.7.3 :007 > "a".bytes
 => [97]
2.7.3 :008 > "z".bytes
 => [122]


#很好玩的一点
2.7.3 :037 > 1..6.class
ArgumentError (bad value for range)
#字串,数字,阵列,杂凑我们自己输入好就会new完成。范围没有。
2.7.3 :038 > (1..6).class
 => Range

一般解题刚接触范围,常见到会用在回圈,例如:

for num in 1..array.size-1 do ... end

#或是..与...差异
2.7.3 :040 > (1..5).to_a
 => [1, 2, 3, 4, 5]
2.7.3 :041 > (1...5).to_a
 => [1, 2, 3, 4]


#另外不用to_a转型写法。
2.7.3 :043 > [*"a".."h"]
 => ["a", "b", "c", "d", "e", "f", "g", "h"]

leetcode.122:Best Time to Buy and Sell Stock II

#第一个是错误答案,第二个正确,主要是因为..与...差异。

def max_profit(prices)
  max_profit = 0
  for i in 0..(prices.size - 1)
    max_profit += (prices[i+1] - prices[i]) if prices[i+1] > prices[i]
  end
  max_profit
end

def max_profit(prices)
  max_profit = 0
  for i in 0...(prices.size - 1)  
    max_profit += (prices[i+1] - prices[i]) if prices[i+1] > prices[i]
    #prices[i+1] > prices[i] && max_profit += (prices[i+1] - prices[i])
    # 上一句可改写成这样,久了会习惯,不习惯就是原本的比较好。
  end
  max_profit
end

习惯多利用each,map

def max_profit(prices)
  max_profit = 0
  prices.each_cons(2) do |price , next_price| 
    next_price > price && max_profit += next_price - price 
  end
  max_profit
end

後面日程会再提到each,map。
若真有比我新的新手路过,先记得初期题型Range比较常被拿来利用,尤其在阵列身上。


杂凑

又爱又恨...
原因是ㄓ跟ㄗ,ㄔ跟ㄘ

先了解长相

2.7.3 :050 > {a: 123, :b => 123}.class
 => Hash
#前面是比较新的写法,後面是箭头式,都对。
2.7.3 :051 >{a: 123, :a => 123}.class
(irb):51: warning: key :a is duplicated and overwritten on line 51
 => Hash
#被覆盖指向了,所以请确定Key没用过。
2.7.3 :052 > {a: 123, "a": 123}.class
(irb):52: warning: key :a is duplicated and overwritten on line 52
 => Hash
#一样被overwritten
2.7.3 :054 > {:a => 123, "a" => 123}.class
 => Hash
#又没问题了....

会这样的原因在昨日的符号。
新手初期手动写杂凑请记得格式统一。
利用运算变成Hash的资料也是帮你统一格式,没理由自己写用两种以上格式。

Hash在学习上除了知道找Key跟Value怎麽处理外,会有很多题型是将资料转成Hash来处理。

2.7.3 :057 > hash = { a: 1, b: 2, c: 3}
 => {:a=>1, :b=>2, :c=>3}
2.7.3 :058 > hash.keys
 => [:a, :b, :c]
2.7.3 :059 > hash.values
 => [1, 2, 3]
#keys与values这两个用复数,这惯例在Rails上也很常见,可以当成一种习惯。

2.7.3 :060 > hash[:a]
 => 1
2.7.3 :061 > hash[:b]
 => 2
2.7.3 :063 > hash.index(2)
 => :b

leetcode001:two_sum
永远劝退的第一题

def two_sum(nums, target)
  new_hash = {}
  nums.each_with_index do |num, index| 
    return [new_hash[num], index] if new_hash.has_key?(num)
    new_hash[target - num] = index
  end
end

leetcode167:two_sum II

def two_sum(numbers, target)
  hash = {}    
  numbers.each_with_index do |num, i|
    return [hash[target - num] +1 , i + 1]  if hash[target - num]
    hash[num] = i
  end
end
#说明,文章连结都有喔。

利用枚举产生的Hash,其中的Key与Value常是会有关联性的,依照这个关联性,当我们知道其中一个值後,能很快速知道另外一个的结果。

例如

2.7.3 :075 > [1, 2, 3, 3, 5, 6, 1, 2, 7].group_by {|num|num}
 => {1=>[1, 1], 2=>[2, 2], 3=>[3, 3], 5=>[5], 6=>[6], 7=>[7]}
2.7.3 :076 > [1, 2, 3, 3, 5, 6, 1, 2, 7].tally
 => {1=>2, 2=>2, 3=>2, 5=>1, 6=>1, 7=>1}

查表法难在建立表格,建好之後的快乐会让你忘记建立时的痛苦。

Hash往往是被介绍的很少,但後面越用用多的资料类型,尤其到Rails後,可以发现所有实体都长的跟Hash没什麽差别,刷题与学习Rails不一定有正比关系,但是练习越多,越不会害怕Hash,越能体会迭代与枚举的好处。


今日提到的。
1.Duck Typing
2.一些曾经解过的leetcode题目。



<<:  Day2 HTML

>>:  Day 02 :zsh 与 shell script

Postman pre-request script & tests

本质上是一样的东西,只是一个是在 request 前执行、一个是在收到 response 後执行,分...

【Day24】Git 版本控制 - 修改 commit 纪录:amend

上一次有提到说,修改 commit 的方式有以下几种: 把 .git 目录整个删除,暴力破解法,请不...

第43篇-IT邦 的 DNS 、凭证都快过期了?!

今天进度 : 鸟哥私房菜 - 第十九章、主机名称控制者: DNS 服务器 今天使用 whois 指令...

[Day4] API开发规格书

看完永丰的API规格书,开始盘点所需之参数。 由规格书可知,呼叫API所需要的参数有Version、...

成为工具人应有的工具包-07 IEHistoryView

IEHistoryView 今天来认识 IEHistoryView 虽然 IE 已经停止更新很久了,...