D-27. 编译直译、动态静态、强型弱型 && Leetcode:Add Digits && Move Zeroes

Ruby是直译语言

程序码要能让电脑读懂,一定会有一个转译过程。

编译(Compiled language):将所有程序码通过编译器(compiler),编译成电脑看得懂的机器码,也就是0与1後,再一起执行。

  • 编译完成的程序,由於是电脑本身能读懂的,所以可以直接运行,例如.exe档,mac的可执行档。
  • 由於是一次执行,在逻辑相同的条件下,运行上也比较快。
  • 程序码在编译时如果有误,就会报错,无法执行,会减少执行时发生错误。
  • 也因编译在一开始就会判定型别,所以多为静态语言,如CC++等。
  • 相比直译,开发较慢一点。

直译(Interpreted language)或称解译:程序码执行时,一行一行的由解译器(interpreter)转换成机器码,并且执行。

  • 这类语言的程序在电脑内,必须有相对应的环境才能执行,也就是VM(台湾|IBM)。
  • 因为有解译的手段,语言设计上可以口语化(交谈式)。
  • 型别判定在执行阶段,所以多为动态语言,如RubyPythonJavascript
  • 由於可完成一小段程序码或一个功能时,执行查看结果,开发上比直译快。
  • 由於边翻译边执行,执行上会比编译语言慢。

Ruby是强型别语言

强弱型别语言最易之区分为,程序是否对资料型态自动转换的程度,较高者为弱型别,较弱者为强型别。

例如:

//JavaScript
Welcome to Node.js v14.15.5.
Type ".help" for more information.
> 123 + "456"
'123456'
//型别自动转换

反之:

#Ruby
2.7.3 :001 > 123 + "456"
TypeError (String can't be coerced into Integer)
#型别无法转换

#需更改如下
2.7.3 :002 > 123.to_s + "456"
 => "123456"
2.7.3 :003 > 123 + "456".to_i
 => 579

弱型别:型别处理感觉上较宽松,较容许资料隐性型别直接转换。优点在於执行较快,但若未注意型别,可能会得到错误的资料。

强型别:型别处理感觉上较严谨,较不容许资料隐性型别直接转换。优点在於资料型别区分清楚,但执行上较慢。
但不是绝对强型或绝对弱型,是比较级的。

Ruby是动态型语言

动态型别语言如之前程序码示范,於执行阶段,会藉由to_i这类程序码,由解译器协助转换型别(於执行期检查型别),相对转换资料类别的方法(函式,功能)较多。

wiki
动态程序语言是高阶程序语言的一个类别,在电脑科学领域已被广泛应用。它是一类在执行时可以改变其结构的语言:例如新的函式、物件、甚至代码可以被引进,已有的函式可以被删除或是其他结构上的变化。动态语言目前非常具有活力。众所周知的ECMAScript(JavaScript)便是一个动态语言,除此之外如PHP、Ruby、Python等也都属於动态语言,而C、C++、Java等语言则不属於动态语言。

静态型别语言则是在编译阶段检查型别。

并不是动态语言就一定是强型别,也不是静态语言就一定弱型别,wiki引言内的PHP即为动态弱型别语言,Ruby则是强型别动态型语言。依照这些特性,与昨日介绍的鸭子型别介绍,前日的Ruby设计风格,可以知道Ruby的优点是较自由、口语化、类别分类清楚、且有较多语法可针对资料类型直接做转换处理。


要快速了解Ruby设计了多少好方法,只有不断地练习。

虽然不是刷题刷得多,一定找得到工作,但是知道的越多,要用时才不用找半天,今天继续分享能感觉到语法懂得多是好处的题型。

Leetcode258.Add Digits
题目连结:https://leetcode.com/problems/add-digits/
题目重点:需要重复处理资料至单个数字为止。
我自己会习惯整理到自己的开发工具内,例如:

# @param {Integer} num
# @return {Integer}
def add_digits(num)

end

puts add_digits(38)  #=> 2
puts add_digits(0)  #=> 0

但建议能习惯於直接在Leetcode上直接操作较好。

题目需要每个数字相加。

2.7.3 :005 > 3 + 8
 => 11
#大於10,要继续处理。
2.7.3 :006 > 1 + 1
 => 2

#将Integer拆成每个数字分开的状态
2.7.3 :032 > 38.to_s.split""
 => ["3", "8"] #喔,还要变成数字...
2.7.3 :034 > ["3", "8"].map {|str|str.to_i}
 => [3, 8]
#再将数字相加後判断是否大於2位数(所以是大於9)
2.7.3 :035 > [3, 8].sum
 => 11
#如果是重复处理,就再如第一条重复处理。
2.7.3 :037 > 11.to_s.split""
 => ["1", "1"]
2.7.3 :038 > ["1", "1"].map {|str|str.to_i}
 => [1, 1]
2.7.3 :039 > [1, 1].sum
 => 2

整理後如下。

def add_digits(num)
  num = num.to_s.split""
  num = num.map {|str| str.to_i}.sum
  num > 9 ? add_digits(num) : num
end

但是如果认识digits这个语法

def add_digits(num)
  num = num.digits.sum
  num > 9 ? add_digits(num) : num
end

2.7.3 :056 > 38.digits
 => [8, 3]

补充

  #利用正整数的除9的余数 == 正整数每个数相加到最後这个原理
def add_digits(num)
  num == 0 ? 0 : (num%9 == 0 ? 9 : num%9)
end

def
  if num == 0
    0
  elsif num%9 == 0
    9
  else
    num%9
end

个人还是喜欢用digits,Integer.digits => 数字的数字们,真的很口语化。


再一题显示多了解语法的好处。
Leetcode283.Move Zeroes
题目连结:https://leetcode.com/problems/move-zeroes/
题目重点:要求不做一个新Array。


def move_zeroes(nums)

end

puts move_zeroes([0,1,0,3,12]) #=> [1,3,12,0,0]
puts move_zeroes([0]) #=> 0

这题是可以用快慢指针解法,我先换一个小一点的例子。

 *          #检查用的指针
[0, 1, 0, 1]
 $          #标记用的指针

先检查第一个数值是0,那我先不动它,标记它是0。

 *          #检查指针
[0, 1, 0, 1]
 $          #零在这里喔

检查第二个。

    * #检查指针到第二个位置了
[0, 1, 0 ,1]
 $

发现不是零,那要交换。
    *       #检查指针
[1, 0, 0 ,1]
    $       #Array中,位置+1 就交换了,所以检查指针是零时标记指针不动,不是零时+1。

继续往前检查。

       *    #检查指针
[1, 0, 0 ,1]
    $       #零在这里喔
    
是零,那检查指针往前。

          * #检查指针
[1, 0, 0 ,1]
    $       #零在这里喔

不是零,跟标记的零交换吧。
          *
[1, 1, 0, 0]
       $    #标记位置+1

检查指针跑完了,也排完了。

#原谅我打字累了,我直接整理吧
def move_zeroes(nums)
  pointer_fast = 0
  pointer_slow = 0
  for pointer_fast in 0..(nums.size-1) do
    if nums[pointer_fast] != 0
      nums[pointer_fast], nums[pointer_slow] = nums[pointer_slow], nums[pointer_fast]
      pointer_slow += 1
    end
  end
  nums
end

换成map.with_index,也感觉没简单很多。

def move_zeroes(nums)
  slow = 0
  nums.map.with_index do |num, index|
    if num != 0
      nums[index], nums[slow], = nums[slow], nums[index]
      slow += 1
    end
  end
end

试试口语化的Ruby语法吧

def move_zeroes(nums)
  # 我要知道我有几个零
  # 把数列中的零都去掉後
  # 从阵列後面加回去
end

def move_zeroes(nums)
  zero_count = nums.count(0)
  nums.delete(0)
  zero_count.times {nums.push(0)}
end
#简单易懂许多

这还不够简单,就试试大神分享的咒语吧!

def move_zeroes(nums)
  #阵列呀,依照0是比较大的往後排吧!
end

def move_zeroes(nums)
  nums.sort_by! { |num| num.zero? ? 1 : 0 }
end

我又开始不正经了

没有错,在一开始,除非刚好看过,怎麽可能知道利用sort_by
没有练习,只有看,可能看完一个class内所有方法时,前一个class的方法就都忘光了。
所以只有练习再练习,没看过的神奇语法,就去了解怎麽用。
多用几次,就会是自己的了。

首先sort_bysort_by!的差异

2.7.3 :058 > str = ["abc", "ab", "a"]
 => ["abc", "ab", "a"]
2.7.3 :059 > str.sort_by {|s|s.length}
 => ["a", "ab", "abc"]
2.7.3 :060 > str
 => ["abc", "ab", "a"] 
#可以发现,虽然有回传["a", "ab", "abc"],但str本身并无改变。

2.7.3 :061 > str.sort_by! {|s|s.length}
 => ["a", "ab", "abc"]
2.7.3 :062 > str
 => ["a", "ab", "abc"]
#多了!後str自身改变了。

再来将程序码改回用do...end,以及不用三元运算子

[0,1,0,3,12].sort_by! do |num|
  if num == 0
    1
  else
    0
  end
end
 => [1, 3, 12, 0, 0]
#依照如果0给1这个比较大的值,不是0的给与0这个比较小的值,依照给予的值来排列。
#即使是写
2.7.3 :071 > [0,1,0,3,12].sort_by! {|nun|nun.zero? ? 999:9}
 => [1, 3, 12, 0, 0]
#也是一样的意思,只是这样写会被念....

如果还是看不懂,没关系,那我们再多看看程序码。

2.7.3 :081 > [5, 2, 7, 8, 3, 1].sort
 => [1, 2, 3, 5, 7, 8]
#sort语法本身就是依照值的大小排
2.7.3 :082 > [5, 2, 7, 8, 3, 1].sort_by {|num| num}
 => [1, 2, 3, 5, 7, 8]
#sort_by,很直觉感觉就是可以依照我们指定的规则来排序,没有就是依照原本小到大的规则。

nums.sort_by! { |num| num.zero? ? 1 : 0 }
# if == 0 那就给你比较大的值 1,if != 0 那就给你比较小的值 0。
#即使改写如下,一样的意思,规则定好枚举方法,block内就会是你的许愿池
2.7.3 :084 > [0,1,0,3,12].sort_by! {|nun| nun != 0 ? 0 : 1}
 => [1, 3, 12, 0, 0]

本日提到的

1.Ruby是直译,动态类型,强型别语言。
2.Ruby真好玩,快来学!

明日开始对Ruby更好玩的部分block做说明,一样会尽量以实作取代说明。


<<:  Genero Package 个别套件与板差简介

>>:  [Day03] CH02:告诉我你是谁——变数的宣告

Day 19 Libraries & TypeScript

前面中场休息的 Project 今天暂且休一天,来介绍一下可以在 JavaScript 跟 Type...

css float

float称为浮动元素,可以想像是加上这个元素的物体都会飘浮在空中 接下来示范一些用法来认识这个元素...

Day20 类别与物件介绍

object---物件(东西)、概念,宇宙间任何具体的东西或抽象的事物 物件导向(object-or...

Django - template filter and tags

context的内容: sub_dual = [{'start':1, 'end':2, 'text...

我们的基因体时代-AI, Data和生物资讯 Overview

大家好,我们的基因体时代是我之前一直在经营的部落格名称,假如对於生物资讯、合成生物学、基因体学、资料...