iT邦幫忙

2021 iThome 鐵人賽

DAY 7
0
自我挑戰組

初級紅寶石魔法師心得分享。系列 第 7

D-23. Self的意思、實體方法與類別方法、Private方法 && Minimum Moves to Equal Array Elements II

Ruby中self的意思?

Ruby Guides
What is self, exactly? It’s a Ruby keyword that gives you access to the current object.

先看個在irb中的self

2.7.3 :100 > def coffee
2.7.3 :101 >   puts self
2.7.3 :102 > end
 => :coffee
2.7.3 :103 > coffee()
main
 => nil

為何印出main?

答案是:Because it’s the name of the top-level object, it’s an object where you’ll find all the methods defined outside a class.
就結束了,問題這個top-level object到底在哪?

在這裡

2.7.3 :001 > is_a?Object
 => true
2.7.3 :002 > methods
 => [:irb_popb, :popb, :jobs, :fg, :source, :help, :inspect, :context, :kill, :exit, :to_s, :irb_quit, :quit, :irb_print_work.....略

Ruby for beginners
The top-level scope is an empty, anonymous object. All Ruby code starts in here.

簡單的說,當執行irb時,irb都會先開好一個最上層的匿名top-level object,可以直接定義一堆方法類別,或是輸入一堆資料,甚至開一個新.rb檔,在運行前讓在irb中的物件暫時的"獨立存在",也是為何當離開重進後會一切需要從新輸入。

有更感覺到為何可以輸入什麼值,就馬上會回傳了嗎?

2.7.3 :003 > 50
 => 50
2.7.3 :004 > "50"
 => "50"
2.7.3 :005 > {}
 => {}

補充:先隨意定義一個method,也可以用下面方式找到喔

2.7.3 :009 > methods.include?(:meth_name)

self在class裡時

class Ashen
  attr_accessor :role
  def initialize(name, role)
    @name = name
    @role = role
  end

  def one
    puts self
  end

  def kill_gwyn
    self.role = "薪王"
    puts self
  end
end

2.7.3 :078 > player = Ashen.new("Ash", "不死者")
 => #<Ashen:0x00007f8ef30d25f8 @name="Ash", @role="不死者">
 
2.7.3 :079 > player.one
#<Ashen:0x00007f8ef30d25f8>
 => nil
#與new出來時的時候記憶體編號相同。

2.7.3 :080 > player.kill_gwyn
#<Ashen:0x00007f8ef30d25f8>
 => nil
#與上面一樣

2.7.3 :082 > player.role
 => "薪王"

所有操作都是指向new出來的player

Access to the current object。

class Ashen
  def self.say_something
    puts "灰燼成雙,則火燃起"
    puts self
  end
end

2.7.3 :013 > Ashen.say_something
灰燼成雙,則火燃起
Ashen
 => nil

self.say_something是定義類別方法的一種寫法,可以看到puts self印出的是類別Ashen

文字遊戲,Ashendefself.say_somethingAshen定義自己說些什麼。
Ashen.is_a? Object #=> true
無論如何self指的就是當前物件。

玩一下itself

2.7.3 :024 > 50.itself == Integer
 => false
2.7.3 :025 > 50.itself == 40
 => false
2.7.3 :026 > 50.itself == 50
 => true
2.7.3 :027 > 50.itself * 10
 => 500
2.7.3 :028 > [1, 2, 3].map{|num| num.itself.to_s}
 => ["1", "2", "3"]

請說明類別方法or實體方法差異

判斷方式如self,看是誰在用,真的就這麼簡單。
所以只介紹怎麼建立類別方法,一種上一段說了,現在講比較好整理的寫法。

昨天腳本有以下這段被標記起來的code。

class << self
  def fake_new
    Elden_ring.new("隱藏大魔王", "玩家", "unlimited")
  end
end

直接改造一下。

class Elden_ring
  def initialize(name = "殭屍", role = "反派", power = 20)
    @name = name
    @role = role
    @power = power
  end

  class << self
    def fake_new
      Elden_ring.new("隱藏大魔王", "玩家", "unlimited")
    end
  end
end

2.7.3 :044 > me = Elden_ring.new
 => #<Elden_ring:0x00007fd511af6ca0 @name="殭屍", @role="反派", @power=20>
 
2.7.3 :045 > me.fake_new
Traceback (most recent call last):
NoMethodError (undefined method fake_new for #<Elden_ring:0x00007fd511af6ca0>)

2.7.3 :046 > Elden_ring.fake_new
 => #<Elden_ring:0x00007fd511af4d60 @name="隱藏大魔王", @role="玩家", @power="unlimited">

fake_new只有Elden_ring能用喔,因為是類別方法。


Ruby中private的用法

昨天的範本,將private的註解取消後,在腳本區的ash.bad_name?(ash.name),就會馬上使檔案無法運行,會出現這樣的錯誤<main>': private method bad_name?' called for #<Elden_ring:0x00007fe03e179758> (NoMethodError)

不是寫在同一頁嗎?也沒有故意拆分兩個檔案(正確應該要分開...),為何還是顯示私有的?
原因是,private讓方法只能在class的定義區內被使用。
反過來將ash.bad_name?(ash.name)註解掉,發現又可以繼續正常運行腳本。

private寫法

class Mine

  private
  def under_private_is_mine
  end
end

也可以這樣

class Mine

  def some_methods
  end

  def i_am_private
  end
  private :i_am_private
end

由於網路上private三兄弟資料非常多,這邊只補充一部分是,類別方法也可以private

2.7.3 :024 > Array.name
 => "Array"
2.7.3 :025 > Integer.name
 => "Integer"

class Test_list
  def self.name
    "中二"
  end
end

2.7.3 :031 > Test_list.name
 => "中二"

不是指這種私有喔!
而是如一般方法的私有。

class Abc

  def self.go_home
    way
  end

  class << self
  private
    def way
      puts "on my way!"
    end
  end
end

2.7.3 :014 > Abc.way #NoMethodError (private method `way' called for Abc:Class)
2.7.3 :015 > Abc.go_home
on my way!
 => nil

2.7.3 :016 > class Bcd < Abc
2.7.3 :017 > end
 => nil
2.7.3 :018 > Bcd.go_home
on my way!
 => nil

是的,類別方法可以私有,但看程式碼應該可以發現privateclass_methods的繼承沒有限制效力。
這不是原理,這是原則喔,不然怎麼物件導向,不然怎麼類別繼承。
看過網路文章有人鑽牛角尖問這個問題,最後的回答是你應該去學其他語言


補充:()有時千萬不要省略。

昨天code很明顯有一個地方是不該省略( )的。
直接這邊改寫

2.7.3 :001 > bad_word = [1, 2, 3]
 => [1, 2, 3]
2.7.3 :002 > "123".split"" & bad_word == []
Traceback (most recent call last)
NoMethodError (undefined method `&' for "":String)
#依照提示修改

2.7.3 :003 > "123".split("") & bad_word == []
 => true

或是像我的醜寫法

2.7.3 :004 > ("123".split"") & (bad_word) == []
 => true

常常code突然跳NoMethodError (undefined method ...,這類資訊,就該先檢查一下是不是()在搞鬼了
另外還有要注意類似數學運算,執行權{}優先權大於[]大於()


腳本區運用到的一些小小小功能

sleep的用途。
暫停程式運行用,也可用在方法內喔。

def hi_and_hello
  puts "hi"
  sleep 2
  puts "hello"
end

執行後會停止運作,不過進入Rails後,前端交給js,不大會再看到了。
記得帶參數,不要只有sleep()

gets.chomp()
這是看一本老書Learn Ruby The Hard Way看到的,因為版本問題不會很推薦看這本書,但是偶爾看看可以發現很多老玩家用的特殊寫法,那些在中文資料內比較不好找到。

gets很明顯是抓取方法。irb可直接利用。

2.7.3 :014 > gets

輸入後會發現畫面換行但是沒動,不用擔心,代表可以開始輸入內容了。

2.7.3 :015 > gets
今天天氣很好,我想要出門去玩!
 => "今天天氣很好,我想要出門去玩!\n"

可以發現多了換行符號\n,我們改輸入gets.chomp()()可省略。

2.7.3 :026 > gets.chomp
今天天氣很好,我想要出門去玩!
 => "今天天氣很好,我想要出門去玩!"

chomp作用是去除抓取字串最後面的跳脫字元。

2.7.3 :034 > gets.chomp
今天天氣\n很好
 => "今天天氣\\n很好"

在中間的沒用喔。

補充chop

2.7.3 :037 > "很重要".chop
 => "很重"
2.7.3 :038 > "很重".chop
 => "很"
2.7.3 :039 > "很".chop
 => ""

puts你不用知道但知道比較酷的技巧?
**irb模式變成程式一部分再輸出有時會有不一樣效果喔
主要說明於method內的puts

請用\n

def test_puts
  puts "123\n456\n789"
end
2.7.3 :075 > test_puts()
123
456
789
#puts "123
#456
#789"  方法內這樣不會出事,比較不好看而已

想要""內有""或特殊符號,%{}

def test_puts
  puts %{"1-9"~!@#$%^&*()_+-=}
end
2.7.3 :079 > test_puts()
"1-9"~!@#$%^&*()_+-=

%w() => 如果不想一直打""

def test_puts
  string = %w(今天 天氣 有夠好 對不對)
  puts string
end
2.7.3 :084 > test_puts()
今天
天氣
有夠好
對不對

puts <<-EOF ....... EOF可以縮排效果

def test_puts
  puts <<-EOF
  一陣操作猛如虎
      一看戰績零杠五
  EOF
end

2.7.3 :122 > test_puts()
  一陣操作猛如虎
      一看戰績零杠五

%s真的想不到什麼地方可以用了..因為有each呀

def test_puts
  puts "%s\n%s" % ["一頓操作猛如虎","一問薪水二百五"]
end
 => :test_puts
2.7.3 :131 > test_puts()
一頓操作猛如虎
一問薪水二百五


def test_puts
  strings = ["床前明月光", "疑似地上霜", "舉頭望明月", "低頭吃便當"]
  strings.each{|string| puts string}
end
2.7.3 :136 > test_puts()
床前明月光
疑似地上霜
舉頭望明月
低頭吃便當

瞭解一些就好,設計師們會幫你煩惱這塊的....


物件更改hash內的key與value

這部分到Rails後,會被Migration取代,畢竟連接著資料庫,需要更安全正確的手段才能動資料庫。
也更不應該隨便手動去更改資料庫的內容。

Has的key相對於每個column名稱,value相對於column的值。
在Ruby時我們有許多方式去更改其中的key或value。

2.7.3 :137 > id_1 = {:name => "索拉爾" , :role => "太陽戰士", :belief => "太陽長男", :power => 250}
 => {:name=>"索拉爾", :role=>"太陽戰士", :belief=>"太陽長男", :power=>250}
2.7.3 :140 > id_1[:name] = "獵龍者"
 => "獵龍者"

#改key
2.7.3 :146 > id_1 = {:name => "索拉爾" , :role => "太陽戰士", :belief => "太陽長男", :power => 250}
 => {:name=>"索拉爾", :role=>"太陽戰士", :belief=>"太陽長男", :power=>250}
2.7.3 :147 > id_1[:title] = id_1.delete :name
 => "索拉爾"
2.7.3 :148 > id_1
 => {:role=>"太陽戰士", :belief=>"太陽長男", :power=>250, :title=>"索拉爾"}

#加入新的key與值的其中一種方法。
2.7.3 :003 > h = {}
 => {}
2.7.3 :004 > h[:a]= 123
 => 123
2.7.3 :005 > h
 => {:a=>123}
2.7.3 :007 > h[:b] = 456
 => 456
2.7.3 :008 > h
 => {:a=>123, :b=>456}

解題的時,一些更改value與key的方法,都會是好方法,但記得到Rails後,不要這樣對待資料庫內的資料。
~~這段純屬碎碎念~


滿一週了,紀念性的來一題中等難度的吧!
今天的Leetcod462. Minimum Moves to Equal Array Elements II
就昨天那題的II,不小心點到,不小心剛好想起最佳開會距離這個議題。
所以之後還是繼續Easy題XD
題目連結:https://leetcode.com/problems/minimum-moves-to-equal-array-elements-ii/
題目重點:隨意選一個加1或減1,其實就是解答。

# @param {Integer[]} nums
# @return {Integer}
def min_moves2(nums)

end

puts min_moves2([1,2,3])  #=2
puts min_moves2([1,10,2,9]) #=16

[1, 2, 3]就可以看到答案。變成一樣其實往中間跑最快。

2 = 3 - 1 #動一步
2 = 1 + 1 #動一步
1 + 1 = 2

換個例子[1, 5, 4]

#4是中位數
2.7.3 :016 > [1, 5, 4].sort!
 => [1, 4, 5]
#直接改變參數本體,之後直接計算,不用再多用到一個變數的記憶。
#往4跑
4 = 1 + 3 #動3步
4 = 5 - 1 #動1步

總共4步,記得有的是+,有的是-,+用x軸來說等於與中心點的位置是負的,-反之。
明明講有正有負就好....

如果不相信這個原理,請想想兩個人見面是往面對面的方向跑快,還是往同一邊跑?
所以n個人,要見面,也是往中心點跑,但這題目中心點是固定的,在這n個人之中,所以選出n個人的中心點。

整理一下

nums.sort!.each
nums.sort!.map #用哪個都好,但是我用完each後直接改map,等等答案就會知道原因了。

muns.size/2 #中間值
|num| num - nums[muns.size/2]
#因為有正有負記得加Anti-Lock Brake System,開玩笑的.abs。
(|num| num - nums[muns.size/2]).abs # 每個都計算與中心點的絕對值

最後記得sum。
整理完如下

def min_moves2(nums)
  nums.sort!.map do |num|
    (num - nums[nums.size/2]).abs
  end
  nums.sum
end

def min_moves2(nums)
  nums.sort!.map {|num|(num - nums[nums.size/2]).abs}.sum
end

今天的重點
1.Ruby中self的意思?
2.請說明類別方法or實體方法差異
3.Ruby中private的用法
Leetcod462. Minimum Moves to Equal Array Elements II

明天講繼承與模組。


上一篇
D-24. attr_accessor 、類別變數與實體變數差異 && Minimum Moves to Equal Array Elements
下一篇
D-22. 繼承(繼承鏈問題)、模組(extend、include、prepend差異) && Add to Array-Form of Integer
系列文
初級紅寶石魔法師心得分享。30

尚未有邦友留言

立即登入留言