Vinova tuyển lập trình viên Mobile & Web ở Hà Nội, lương $300-1000

Article: 10 điều LTV Java cần biết về Ruby 3453

Ruby căn bản
dangtrieu.myopenid.com 5
Updated over 4 years ago
“Một ngôn ngữ không có ảnh hưởng mới nào tới cách tư duy trong lập trình của bạn thì không đáng để học.”—Alan Perlis

“Ruby là một ngôn ngữ mà tôi tin rằng sẽ thay đổi tư duy lập trình của bạn.”—javaeater

Trước hết đây không phải là bài viết so sánh kiểu “Ruby hay hơn, tốt hơn Java”, như đã từng được bàn luận sôi nổi trên diễn đàn Java Vietnam. Trái lại, mục đích chính của bài viết là giúp LTV Java đang học Ruby tiếp cận nhanh hơn ngôn ngữ này, thông qua một số cách như so sánh điểm tương đồng giữa 2 ngôn ngữ. Nếu chưa học Ruby, với căn bản Java chắc bạn cũng hiểu được đa phần bài viết này.

Đây là bản chuyển ngữ tiếng Việt, đã được điều chỉnh cho dễ hiểu đối với lập trình viên Việt Nam. Bản gốc ở đây.

Điều 10: Qui cách viết chương trình Ruby

Mỗi ngôn ngữ đều có một số qui cách viết chương trình như cách đặt tên hàm, tên biến… mà qua đó các LTV có kinh nghiệm chỉ cần nhìn sơ qua là có thể đoán biết chức năng của các hàm, biến này là gì.

Đây là một số qui cách (conventions) trong Ruby. Có một số là bắt buộc và một số chỉ là qui định chung trong cộng đồng Ruby.
  • Các từ trong tên lớp (class) viết hoa và không có dấu ngăn cách.
  • Các hàm trong class viết thường và các từ cách nhau bằng dấu gạch dưới.
  • Các biến có cách đặt tên tương tự như tên hàm.
  • methods_asking_a_question?: Các hàm có tính query, trả về true hoặc false.
  • slightly_dangerous_methods!: Các hàm có thể gây “nguy hiểm”.
  • @instance_variables: Các biến riêng.
  • $global_variables: Các biến chung, biến toàn cục.
  • SOME_CONSTANTS or OtherConstants: Các hằng.

Điều 9: Mọi thứ trong Ruby đều là các Object (hay đối tượng)

Trong đa số các ngôn ngữ, các kiểu như integer hay double không phải là các object, còn trong Ruby gần như tất cả đều là object. Thế nên chúng ta có vài điều thú vị thế này:

+ Tạo một Array: Array là một tên hằng gắn với lớp (class) Array. Việc tạo một object mới không cần đòi hỏi một cú pháp nào đặc biệt, chỉ cần gọi hàm new của object ấy:

a = Array.new

+ Sản xuất object: Vì các object có thể “tự sinh sản”, nên nó chính là kiểu object công xưởng (factory). Chúng ta hãy làm thử một xường sản xuất nhỏ thế này:

def create_from_factory(factory)
  factory.new
end
obj = create_from_factory(Array)

+ Làm đủ thứ với integer: Ngay cả số integer cũng là một object đầy đủ. Nên bạn có thể viết thế này:

0.zero? # => true
1.zero? # => false
1.abs   # => 1
-1.abs  # => 1

1.methods       # => xem danh sách các hàm của object 1
2.+(3)          # => 5 (giống như 2+3)
10.class        # => Fixnum
(10**100).class # => Bignum

+ nil là một object: Trong Java null không có nghĩa là “không có tham chiếu tới” object đó, nếu bạn cố gọi hàm từ một null object thì sẽ gặp lỗi. Trong Ruby, nil cũng là một object, nên bạn sẽ không bao giời gặp lỗi “null pointer error”.

a = nil
a.nil?     # => true
a.methods  # => danh sách các hàm của nil

+ Nhưng lưu ý: Tên biến không phải là object. Bạn không thể có một biến trỏ tới một biến khác. (Tuy có cách đi vòng để thực hiện điều này nhưng gần như không có ai cần tới nó trong Ruby).

Điều 8: (Hầu hết) Mọi hoạt động đều thông qua các message.

Mọi quá trình tính toán trong Ruby đều trải qua các bước:
  • Gán tên vào các object.
  • Chạy các cấu trúc logic (như if/else, while) hay các toán tử (như .defined?).
  • Gửi các message tới các object.

Đây là ví dụ về các message:

string.index("x")
    Gửi :index (với đối số "x") 

string.length
    Gửi :length (không có đối số) 

run_status_reports
    Gửi :run_status_reports (tới self) 

1 + 2
    Gửi :+ (với đối số 2) tới object 1 

array[i]
    Gửi :[] (với đối số i) tới array

Lưu ý: Các message trên (như :index) bắt đầu bằng dấu hai chấm, nghĩa là dấu hai chấm là một phần của message. Các message này trong Ruby gọi là symbol.

Với LTV Java thì obj.method() là tìm một hàm và chạy nó, còn trong Ruby obj.method có nghĩa là gửi một message đến object đó. Vậy sự khác biệt là gì? Đó không phải là sự khác biệt lớn nhưng rất quan trọng.

Chúng ta hãy xem một lớp có tên là VCR (Video Cassettes Recorder – tức là cái đầu máy video), lớp này ghi lại tất cả các message người ta gửi tới và chuyển chúng tới một object khác.

class VCR
  def initialize
    @messages = []
  end

  def method_missing(method, *args, &block)
    @messages << [method, args, block]
  end

  def play_back_to(obj)
    @messages.each do |method, args, block|
      obj.send(method, *args, &block)
    end
  end
end

Rồi cho chạy thế này:

vcr = VCR.new
vcr.sub!(/Java/) { "Ruby" }
vcr.upcase!
vcr[11,5] = "Universe" 
vcr << "!" 
string = "Hello Java World" 
puts string
vcr.play_back_to(string)
puts string

Kết quả sẽ như sau:

Hello Java World
HELLO RUBY Universe!

Hiểu về message sẽ giúp bạn làm được nhiều điều thú vị.

Điều 7: Ruby linh hoạt hơn là bạn tưởng

Một trong những điều làm cho Java hấp dẫn hơn C++ là linh hoạt (dynamic) của ngôn ngữ này. Bạn có thể (trong Java) tải các lớp lúc chạy, tìm các lớp và hàm của một object và thậm chí gọi các hàm mà bạn vừa mới được biết đó (xin đọc thêm về Reflection của Java).

Nhưng tính linh hoạt của Ruby còn hơn Java vài bậc. Ví dụ:

Reflection dễ dàng hơn

+ Tạo object với Java reflection:

public static Object create(Class c, String value)
throws Exception
{
  Constructor ctor = c.getConstructor(
    new Class[] { String.class } );
  return ctor.newInstance(
    new Object[] { "Hello" } );
}

public static void main (String args[])
throws Exception
{
  Greeting g = (Greeting) create(
    Greeting.class, "Hello");
  g.show();
}

+ Còn đây là cách làm với Ruby:

def create(klass, value)
  klass.new(value)
end

g = create(Greeting, "Hello")
g.show

Lớp mở

Có thể đưa một hàm vào object bất kỳ lúc nào, ngay cả với những object dạng built-in như Integer:

class Integer
  def even?
    (self % 2) == 0
  end
end

p (1..10).select { |n| n.even? }
# => [2, 4, 6, 8, 10]

Hàm singleton

Hàm singleton có thể định nghĩa ở cấp object (thay vì cấp class như Java).

class Dog
end

rover = Dog.new
fido = Dog.new

def rover.speak
  puts "Red Rover" 
end

rover.speak  # => "Red Rover" 
fido.speak   # => NoMethodError

Hooks

Hook (cái móc câu) cho phép “móc” vào để nắm quyền điều khiển chương trình lúc nào bạn thích. Có thể làm thế này trong Ruby.

class MyClass
  def MyClass.method_added(name)
    puts "Adding Method #{name}" 
  end

  def new_method
    # Yada yada yada
  end
end

Kết quả là:

Adding Method new_method

Code Eval

Viết code (mà code này) có thể tự lượng giá (evaluate) chính mình:

class Module
  def trace_attr(sym)
    self.module_eval %{
      def #{sym}
    printf "Accessing %s with value %s\n",
      "#{sym}", @#{sym}.inspect
    @#{sym}
      end
    }
  end
end

class Dog
  trace_attr :name
  def initialize(string)
    @name = string
  end
end
Dog.new("Fido").name  # => Accessing name with value "Fido" 

Điều 6: Object Ruby là strongly-typed, không phải là statically-typed

Nói chung trong Ruby việc khai báo kiểu, ép kiểu không bao giờ là vấn đề.

+ Đây là 1 hàm tính giai thừa (fractorial) của một số viết bằng Java:

public class Fact {
  static long factorial(long n) {
    long result = 1;
    for (long i=2; i<=n; i++) 
      result *= i;
    return result;
  }
  public static
    void main (String args[]) {
    System.out.println(factorial(2));
    System.out.println(factorial(21));
  }
}
Khi chạy cho kết quả là:
2
-4249290049419214848

Kết quả giai thừa của 21 sai vì kết quả vượt quá khả năng chứa của kiểu long.

+ Còn của Ruby:

def factorial(n)
  result = 1
  (2..n).each do |i|
    result *= i
  end
  result
end

x = factorial(2)
y = factorial(21)
puts x
puts y
puts x.class
puts y.class

Cho kết quả là:

2
51090942171709440000
Fixnum
Bignum

Điều 5: Bạn không phải quan tâm tới interface trong Ruby

Trong Ruby có một nguyên tắt thế này “Nếu vật gì (object) có thể đi như vịt và kêu như vịt thì bạn có thể coi nó là vịt. Đừng quan tâm tâm nó thực sự là gì”.

Xin xem đoạn code sau:


   class Duck

          def talk() puts "Quack" end

 end

 class DuckLikeObject

        def talk() puts "Kwak" end

  end

 flock = [

       Duck.new,

       DuckLikeObject.new ]

     flock.each do |d| d.talk 
   

Như bạn thấy bạn có thể “điểm mặt” từng con vịt trong đàn (flock) và bắt nó kêu “cạc” hay “kẹc” tùy theo đó là giống vịt nào, miễn là trong object đó có hàm talk(). Trong Java bạn sẽ phải tạo một interface có hàm talk() mà tất cả các loại vịt muốn kêu được sẽ phải implement cái interface này.

Điều 4: Trộn code bằng Mix-in

Mặc dù trong Ruby không có interface nhưng có thể định nghĩa các mix-in ở cấp Module. Vậy module là gì?

  • Module giống như một namespace.
  • Nó có thể có các hàm (giống như class).
  • Nhưng không giống như class nó không thể có instance.
  • Và bạn có thể trộn module vào class. Khi ấy các hàm trong module sẽ trở thành các hàm trong class.

Chúng ta sẽ xem hàm này, nó có các biểu thức để so sánh hai object cùng loại:


class Pair

  attr_accessor :first, :second

  # ...

  def <(other)
    (first < other.first) ||(first == other.first && second < other.second)
  end

  def >(other)
    other < self
  end

  # Other methods defined in terms of less than:
  #     <=, >=, ==
end

Nhưng nếu có 1 hàm thường được dùng bạn có thể định nghĩa trong module và trộn vào hàm này như sau:


module ComparableUsingLess

  def >(other)

    other < self

  end

  # Other methods defined in terms of less than:

  #     <=, >=, ==

end

class Pair
  include ComparableUsingLess
  attr_accessor :first, :second
  # ...
  def <(other)
    (first < other.first) ||
  end(first == other.first && second <other.second)

end

Điều 3: Tính bao đóng khép kín (embrace closures)

Tính bao đóng trong Ruby rất kín. Ví dụ:

Duyệt danh sách:

.each do |item| puts item end

Đọc nội dung file:

file_contents = open(file_name) { |f| f.read }

Phản hồi:

widget.on_button_press { puts “Got Button Press” }

Điều 2: ri và irb là những người bạn của Ruby

ri -> là lệnh dùng để xem thông tin của các object Ruby chuẩn. irb -> là tiện ích console để trực tiếp gõ lệnh và biên dịch tức thời.

Ví dụ: xem thông tin về Array:


$ ri Array

---------------------------------------------------------- Module: Array

     Arrays are ordered, integer-indexed collections of any object.

     Array indexing starts at 0, as in C or Java. A negative index is

     assumed to be relative to the end of the array---that is, an index

     of -1 indicates the last element of the array, -2 is the next to

     last element in the array, and so on.

------------------------------------------------------------------------

Includes:

---------

     Enumerable(all?, any?, collect, detect, each_with_index, entries,

     find, find_all, grep, include?, inject, map, max, member?, min,

     partition, reject, select, sort, sort_by, to_a, zip)

Class methods:

--------------

     [], new

Instance methods:

-----------------

     &, *, +, -, <<, <=>, ==, [], []=, assoc, at, clear, collect,

     collect!, compact, compact!, concat, delete, delete_at, delete_if,

     each, each_index, empty?, eql?, fetch, fill, first, flatten,

     flatten!, frozen?, hash, include?, index, indexes, indices, insert,

     inspect, join, last, length, map, map!, nitems, pack, pop, push,

     rassoc, reject, reject!, replace, reverse, reverse!, reverse_each,

     rindex, select, shift, slice, slice!, sort, sort!, to_a, to_ary,

     to_s, transpose, uniq, uniq!, unshift, values_at, zip, |

Hay hỏi về hàm last của Array:


$ ri Array#last

------------------------------------------------------------- Array#last

     array.last     =>  obj or nil

     array.last(n)  =>  an_array

------------------------------------------------------------------------

     Returns the last element(s) of _self_. If the array is empty, the

     first form returns +nil+.

        [ "w", "x", "y", "z" ].last   #=> "z" 

Tất cả lệnh này và các lệnh khác đều có thể thực hiện trên irb.

Điều 1: Không cần phải viết nhiều code

Nói chung với Ruby bạn sẽ phải viết ít code hơn nhiều để giải quyết cùng một vấn đề so với các ngôn ngữ khác.

Những điều có thể bạn cần biết khác:

  • Các namespace (các class và module) không phụ thuộc vào các package.
  • Có rất nhiều hàm built-in trong String.
  • ”. (dot) khác với” (double colon).
  • Có một dự án đưa Ruby vào Java: JRuby
  • Có những điểm giống và khác nhau giữa một hàm static trong Java và một hàm trong class của Ruby.
  • finally trong Java có tên ensure trong Ruby
  • flexible quoting.

Theo 10 Things Every Java Programmer Should Know about Ruby

1 2 3 

Editors
id.cntt.tv/javaeater 1
id.cntt.tv/[Anonymous] 9
dangtrieu.myopenid.com 5

Comments

id.cntt.tv/[Anonymous] 9
over 4 years ago
Trong bài dịch 10 điều lập trình viên Java nên biết về ruby có chỗ ghi qui ước đặt tên biến, trong đó liệt kê các loại biến gồm có biến instant, @static, $global. Biến $global là loại biến hoạt động như thế nào em không biết. Nhờ mấy anh chị giải thích thêm dùm em với. Hàm dạng slightly_dangerous_methods! em cũng chưa gặp lần nào. Mấy anh chị chỉ thêm dùm em hàm này với(ví dụ).
alide.myopenid.com 32
over 4 years ago
1. Khi muốn khai báo x biến toàn cục, thì viết $x. Ví dụ:


$x = 10

def f
  puts $x
end
2. Ví dụ:

s1 = 'xyz'

s2 = s1.capitalize
# Đến đây s2 có giá trị là XYZ, còn s1 vẫn có giá trị là xyz
puts s1
puts s2

s3 = s1.capitalize!
# Đến đây giá trị của s1 bị biến thành XYZ
puts s3
puts s1

Method nào nguy hiểm (như thay đổi trạng thái của object chính nó như ví dụ trên), thì để cảnh báo người ta thiết kế là thêm dấu ! vào cuối.

id.cntt.tv/[Anonymous] 9
over 4 years ago
Cho em hỏi biến toàn cục $global và biến toàn cục của lớp @instant_variable có gì khác nhau?
ngocdaothanh.myopenid.com 174
Updated over 4 years ago
Trong câu hỏi em đặt có vài cái sai:
  • Sai chính tả, instance chứ k phải instant.
  • Có cái gọi là “biến của lớp” (class variable) chứ k có cái gọi là “biến toàn cục của lớp”.

Ngoài ra, ngôn ngữ Java em viết hoa, tại sao ngôn ngữ Ruby thì em k biết hoa nhỉ?

Biến của lớp được viết có 2 dấu a còng, ví dụ @@my_class_variable. Biến của instance viết có 1 dấu a còng, ví dụ @my_instance_variable.

Trong Ruby, k thể truy cập trực tiếp biến của class hay instance, mà đều phải thông qua method (phương thức). Trừ điều này ra, các loại biến rõ ràng là khác nhau ở tầm ảnh hưởng: biến toàn cục có tác dụng trong toàn bộ chương trình, biến instance chỉ có tác dụng trong instance…

You must login to be able to comment

Uploaded files

No file uploaded yet

You must login to be able to upload

Nhà tài trợ:

Mọi người đều tự do viết bài, sửa bài của người khác, và bình luận ở trang web này. Bạn muốn chủ động tạo bài mới để chia sẻ kinh nghiệm với mọi người? Xin click link ở dưới.

Create new content