D-24. attr_accessor 、类别变数与实体变数差异 && Minimum Moves to Equal Array Elements

本篇有一个区块的code,是一些常见问题在code中的长相以及用法,里面有一个非常阳春的名字检测器,可以利用运行时,使用者输入的名字来检测。
code中会看到两种符号#**@@

#**部分就是常见问题。
请说明attr_accessor 今天内容有
类别变数,实体变数差异? 今天内容有
Ruby中self的意思?
请说明类别方法or实体方法差异
Ruby中private的用法

#@@部分只是单纯想分享。
initialize与new的差别? 今天内容有
参数加等号。 今天内容有
yield if block_given? 今天内容有
脚本区会一起说到以下内容gets.chomp()sleep的用途
外部脚本可不可以直接改物件的资料。

如果愿意,可以执行看看,单纯想看常见问题,可直接略过。

class Elden_ring
  attr_accessor :name, :role, :power  #**

  @@bad_word = ["脏", "坏", "衰", "呵"]  #**

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

  def named
    yield if block_given? #@@
    new_name = gets.chomp() #@@
    if bad_name?(new_name)
      puts "名字含有不好的字元或空白,请重新输入"
      named() #@@
    else
      self.name = new_name #**
      puts %{是的! 原来他叫"#{self.name}"!} #@@
    end
  end

  # class << self  #类别方法  #**
  #   def fake_new
  #     Elden_ring.new("隐藏大魔王", "玩家", "unlimited")
  #   end
  # end

  # private  #**
  def bad_name?(new_name)
    ((new_name.split"") & @@bad_word) != [] || new_name == ""
  end
end

##下面是脚本区。 也可将两个区块分开,利用`require`来运行

gwyn = Elden_ring.new("葛温", "薪王", "is Over 9000!")
ash = Elden_ring.new("不死镇杂鱼", "战士")

ash.bad_name?(ash.name) #测试private用

puts "#{gwyn.name},祂的职业是#{gwyn.role}。
他的力量#{gwyn.power}"

sleep 2 #@@

puts "\n有一天,有一个#{ash.name}。","是一个力量只有#{ash.power}的#{ash.role}杂鱼。"

sleep 2

puts %{\n#{ash.name}它身为杂鱼,平时非常羡慕"#{gwyn.name}"的存在
它希望自己能像"#{gwyn.name}"一般,成为'神族的一员'!
}

sleep 2

puts "%s\n%s" % ["这天,它做出了杂鱼不可能会出的一件事!", "它坐下冥想...."]
sleep 2

["一年过去了...", "两年过去了...", "三年...", "四年...", "整整五年的时间。"].each{|str|puts str; sleep 2}

ash.named do
  prompt = '> '
  puts "它想起了它拥有一个古老的名字!"
  puts %{#请帮忙输入新名字#}
  print prompt
end

sleep 2
puts "感谢参与测试!"

# boss = Elden_ring.fake_new  #测试类别方法用

# puts boss.name

# ash.fake_new

attr_accessor

是一个Module类别定义出来的方法,由於许多类别常需有取得器(getter)设定器(setter)的需求,以方便操作改写资料与读取,所以直接设计attr_accessor方法,不是取代getter与setter,是会直接帮你生成这两项的相对应方法,常直接写在类别上方,也更清楚这类别中有哪些资料可以取得及设定。

如果自己设定gettersetter,名字随意取也可以,但依照惯例尽量取相同例如下方示范,def name就给@name,这习惯到Rails後也是,保持这习惯,开发速度也会比较快。
不用眼睛花掉还找不到当初自己设定的名字是什麽!

用途?

#取代getter与setter
class Fake_class
  def initialize(name = "金三角")
    @name = name
  end

  def name   #getter methods,取得物件内资料。
   @name
  end
  
  def name=(new_name) #setter methods,设定物件内资料。
   @name = new_name
  end

  def change_name(name)  #有setter 这类方法才能执行
   @name = name
  def
end

2.7.3 :052 > abc = Fake_class.new
 => #<Fake_class:0x00007f923942cc28 @name="金三角">
2.7.3 :053 > abc.name
 => "金三角"
2.7.3 :054 > abc.change_name("泰山")
 => "泰山"
2.7.3 :055 > abc.name
 => "泰山"

使用attr_accessor後,就可以少设定

class Fake_class
  attr_accessor :name

  def initialize(name = "金三角")
    @name = name
  end
end

irb输入完後可以输入.methods,可以看到:name 与:name= 方法。

attr_writer=> 只生成setter
attr_reader=> 只生成getter

如果可以,不要因为懒惰而都只用attr_accessor,有些资料确定只能读取,而不希望被改变就使用attr_reader,有些资料可以更动,但不希望被读取那就attr_writer,虽然进入框架後(不只是Rails),可能也不会再需要手动设定这些,但是知道自己哪些资料该怎麽处理的观念还是要有。


类别变数,实体变数差异?

不确定变数有几种,可以请看看龙哥的,为你自己学Ruby on Rails:变数(variable)

少用类别变数

这是实体变数@variable

class Evil_soul
  @name = "恶魔灵魂"

  def self.name
    p "#{@name}!"
  end
end

2.7.3 :074 > Evil_soul.name
"恶魔灵魂!"
 => "恶魔灵魂!"

#建立一个子类别
class Dark_soul < Evil_soul
  @name = "黑暗灵魂"
end

2.7.3 :078 > Dark_soul.name
"黑暗灵魂!"
 => "黑暗灵魂!"

两个类别的@name不会互相干扰
即使继承後也一样。

2.7.3 :079 > Evil_soul.name
"恶魔灵魂!"
 => "恶魔灵魂!"

如果使用类别变数@@variable

class Evil_soul
  @@name = "恶魔灵魂"

  def self.name
    p "#{@@name}!"
  end
end

2.7.3 :087 > Evil_soul.name
"恶魔灵魂!"
 => "恶魔灵魂!"

class Dark_soul < Evil_soul
  @@name = "黑暗灵魂"
end

#一被继承,如果新类别也有用这个类别变数,类别变数马上改变。
2.7.3 :091 > Evil_soul.name
"黑暗灵魂!"
 => "黑暗灵魂!"

少用类别变数比较好,避免类别被继承後,类别变数会马上改变,因为类别变数在同一条继承链上所有的类别是共用的。实体变数作用在单一类别内,而且两实体间互相不受影响。
当然更不适合直接用全域变数,这只是测试,我只放四个字,如果是很多内容的资料,应该放在别处。


initializenew的差别?

先设定一个class。

class Init_test
  def some(name = "测试", methods ="200种")
    @name = name
    @methods = methods
  end
end
#这个类别new之後,只有给记忆体编号。
2.7.3 :118 > test = Init_test.new
 => #<Init_test:0x00007feb2a377dd8>
2.7.3 :068 > Init_test.some
Traceback (most recent call last):
NoMethodError (undefined method `some' for Init_test:Class)
#这样设定some是实体方法,Init_test用不了。

可以发现,故意设定一种内容跟initiailize方法,是毫无反应的。

class Init_test
  def initialize(name = "测试", methods ="200种")
    @name = name
    @methods = methods
  end
end
2.7.3 :126 > test = Init_test.new
 => #<Init_test:0x00007feb2a30e1f8 @name="测试", @methods="200种">

用initialize才有除了记忆体编号外,有其他"数值"存在。
也代表initialize与new有"联动"。

#如果这样是会动。
class Init_test
  def new(name = "测试", methods ="200种")
    @name = name
    @methods = methods
  end
end
2.7.3 :133 > Init_test.new
 => #<Init_test:0x00007feb2a366948 @name="测试", @methods="200种">

但这样等於你把Ruby原本设定好的new方法更动了,请不要这样。

new方法使用时,会自动调动initialize方法,利用初始化来,建构物件的"长相",所以我们更动initialize就会自动调整到new方法。而如果直接动new方法,那你得先确认new方法里会不会有你不知道的内容了。

initialize在其他语言是建构子的概念(英语: Constructor,有时简称 ctor)


参数加等号 == 参数预设值

def some_method(num = "123")
  num.to_i
end

这样代表如果我们没输入参数,会自动以预设值处理。

2.7.3 :099 > some_method()
 => 123
2.7.3 :100 > some_method(567)
 => 567
#进入有连接资料库的阶段後,要更注意资料栏位的Type,以及自己设定的方法之使用对象。
2.7.3 :111 > some_method([1, 2, 3])
Traceback (most recent call last):
NoMethodError (undefined method `to_i' for [1, 2, 3]:Array)

名字检验器

虽然阳春,还是有几个小小小细节说明一下。
原本code长这样。

def named
    prompt = '> '
    puts "它想起了它拥有一个古老的名字!"
    puts %{#请帮忙输入新名字#}
    print prompt
    new_name = gets.chomp()
    if ((new_name.split"") & $bad_word) != [] || new_name == ""
      puts "名字含有不好的字元或空白,请重新输入"
      named()
    else
      self.name = new_name
      puts %{是的! 原来他叫"#{self.name}"!}
    end
  end

  #下面这四行,不是没有意义,但我写在方法里,等於写死了台词。
  prompt = '> '
  puts "它想起了它拥有一个古老的名字!"
  puts %{#请帮忙输入新名字#}
  print prompt

  #而我使用yield出去後,如果我想换台词,只需要在脚本区内用到的地方,其block区更改内容即可。
  object.named {puts "这样便利多了"}

yield if block_given?named方法又出现了named()

方法内再执行一次同方法算是常见手法,等於是跑回圈的一种,可以不用再看到loop,while,for..in。用多了会发现很好用。

yield if block_given因为我只是请方法再帮我执行一次方法,我第二次并没有给予block。
yield就是去block
要提这个是与rails里的错误处理机制一样,属於比较柔性的处理方式。

回到code。

  ((new_name.split"") & $bad_word) != [] || new_name == ""

常用会用到的行为,不如直接多建立一个方法。
大家都会用到的行为,不如直接建立一个模组。

所以直接建立了一个bad_name?()

  def bad_name?(new_name)
    ((new_name.split"") & @@bad_word) != [] || new_name == ""
  end

可以看到整个检验器非常简单,不是因为我检测资料只有4个字,是因为功能只有拆开输入的字串,再去与检测资料比对,而如果是希望检测名字内有没有敏感的词句,或敏感时事人物名字时,又该如何处理。
那当然是我另外一篇文章的事了

明日从self继续


今日的Leetcod.453. Minimum Moves to Equal Array Elements
题目连结:https://leetcode.com/problems/minimum-moves-to-equal-array-elements/
题目重点:一次可以动n-1个元素来加1(今天是单纯数学题。)

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

end

puts min_moves([1,2,3]) #=> 3
puts min_moves([1,1,1]) #=> 0

炼金术士题..

sum = 初始所有数组和
n = 数组长度
m = 增加次数
x = m次完後会等於的数字
x * n = 最後数组的总和
min = 数组中最小的数
关系如下
sum + m*(n - 1) = x * n
由於增加数都是1,所以最後会等於的数字x = min + m,带入上一行公式。
sum + m*n - m = min*n + m*n
sum - m = min*n
m现在是我们唯一不知道的
sum - min*n = m
解完!

def min_moves(nums)
  nums.sum - (nums.min * nums.size)
end


今天重点
1.说明attr_accessor
2.类别变数,实体变数差异?
3.Leetcod.453. Minimum Moves to Equal Array Elements


<<:  @Day6 | C# WixToolset + WPF 帅到不行的安装包 [自订页面-官方UI页面结构]

>>:  .NET Core第6天_如何将asp.net core应用部属到IIS_透过visual studio

Proxmox VE 网路进阶设定 (Bridge、LACP、VLAN)

在规模较大的企业网路中,为了避免单点故障会采用 LACP 的方式将多条线路聚合在一起使用,除了增加...

Epic Games 跟 Apple 的诉讼对小开发商有什麽影响?

前天 Epic 跟 Apple 的诉讼第一次出结果,Apple 被判要在 90 天内开始允许所有 A...

Day15:15 - 购物车服务(3) - 後端 - 购物车数量增减、删除API

Kamusta,我是Charlie! 在Day14中,我们完成了前端的购物车商品显示跟加入购物车,而...

Day09 - 用 Cloud Run 部属 Serverless 容器应用

什麽是 Serverless ? 若要将应用程序部属到生产环境,会需要考虑很多问题,包括计算资源是否...

Day04 关於分散式系统的一些概念 (一)

今天开始要讲分散式系统的一些概念罗。 影片在此: Day04_关於分散式系统的一些概念 (一) ...