2011-01-20 21 views
14

Tôi đã làm việc thông qua Ruby Koans và làm cho nó thành about_triangle_project.rb, trong đó bạn được yêu cầu viết mã cho một phương thức, tam giác.Một giải pháp thanh lịch hơn cho tam giác của Ruby Koans.rb

Mã cho các mặt hàng này được tìm thấy ở đây:

https://github.com/edgecase/ruby_koans/blob/master/koans/about_triangle_project.rb

https://github.com/edgecase/ruby_koans/blob/master/koans/triangle.rb

Trong triangle.rb, tôi tạo ra các phương pháp sau đây:

def triangle(a, b, c) 
    if ((a == b) && (a == c) && (b == c)) 
    return :equilateral 
    elsif ((a == b) || (a == c) || (b == c)) 
    return :isosceles 
    else 
    return :scalene 
    end 
end 

Tôi biết từ việc đọc Chris Pine của "Học cách lập trình" luôn có nhiều cách để làm việc. Mặc dù mã trên hoạt động, tôi không thể không nghĩ rằng có một cách thanh lịch hơn để làm điều này. Liệu có ai sẵn sàng đưa ra những suy nghĩ của họ về cách họ có thể làm cho một phương pháp như vậy hiệu quả hơn và nhỏ gọn?

Một điều khác mà tôi tò mò là tại sao, để xác định tam giác đều, tôi không thể tạo điều kiện (a == b == c). Đó là bằng chứng cho tam giác đều nhưng Ruby ghét cú pháp. Có một lời giải thích dễ dàng là tại sao đây là?

+1

'==' là toán tử chấp nhận các giá trị (như '*' hoặc '/'). nó trả về 'true' hoặc' false'. nó là bất hợp pháp vì không gây nhầm lẫn (ví dụ: 1 == 1 == 1' sẽ đánh giá là 'false' vì nó tương đương với' (1 == 1) == 1'). – glebm

+1

Bạn có thể đã lưu một chút mã bằng cách sử dụng thuộc tính transitive cho ': equilateral': (a == b) && (b == c) – pkananen

+0

Python hỗ trợ cú pháp" a == b == c "(hoặc thậm chí "a

Trả lời

6
def triangle(a, b, c) 
    if a == b && a == c    # transitivity => only 2 checks are necessary 
    :equilateral 
    elsif a == b || a == c || b == c # == operator has the highest priority 
    :isosceles 
    else 
    :scalene       # no need for return keyword 
    end 
end 
+0

Cảm ơn bạn, glebm! (Và cho câu trả lời ở trên, quá.) – erinbrown

+3

bạn có nghĩa là transitivity :) – Anurag

55

Có một lời giải thích đơn giản tại sao đó là:

== trong Ruby là một nhà điều hành, thực hiện một chức năng cụ thể. Các toán tử có các quy tắc để xác định thứ tự mà chúng được áp dụng - ví dụ: a + 2 == 3 đánh giá phần bổ sung trước khi kiểm tra bình đẳng. Nhưng chỉ có một toán tử tại một thời điểm được đánh giá. Việc kiểm tra bình đẳng là không có ý nghĩa, bởi vì kiểm tra bình đẳng đánh giá là true hoặc false. Một số ngôn ngữ cho phép điều này, nhưng nó vẫn không hoạt động đúng, vì sau đó bạn sẽ đánh giá true == c nếu ab bằng nhau, điều này rõ ràng là không đúng ngay cả khi a == b == c trong thuật ngữ toán học.

Đối với một giải pháp thanh lịch hơn:

case [a,b,c].uniq.size 
when 1 then :equilateral 
when 2 then :isosceles 
else  :scalene 
end 

Hoặc, thậm chí ngắn hơn (nhưng ít có thể đọc được):

[:equilateral, :isosceles, :scalene].fetch([a,b,c].uniq.size - 1) 
+0

Cảm ơn bạn, chuck! Tôi đã không đạt được bằng cách sử dụng trường hợp, uniq hoặc lấy trong koans, nhưng đó là cực kỳ mát mẻ (và tại sao tôi yêu Ruby!). – erinbrown

+3

+1 cho 'uniq.size'; đó là thanh lịch. Thú vị là bạn đã chọn sử dụng 'fetch', như' [...] [[...]. Uniq.size] 'là hợp lệ. – Phrogz

+0

@ Phrogz: Tôi đã viết nó theo cách đó lúc đầu, nhưng nó chỉ đơn giản là không thể đọc được, giống như loại mã Perl mà mọi người luôn làm cho niềm vui, vì vậy tôi đã tìm thấy 'fetch' ít nhất xấp xỉ một cái gì đó tôi muốn đọc. – Chuck

5

tôi mượn kỹ thuật uniq.size mát của Chuck và làm việc nó thành một giải pháp oo . Ban đầu tôi chỉ muốn trích xuất xác nhận đối số làm mệnh đề bảo vệ để duy trì nguyên tắc trách nhiệm duy nhất, nhưng vì cả hai phương thức đều hoạt động trên cùng một dữ liệu, tôi nghĩ chúng thuộc về nhau trong một đối tượng.

# for compatibility with the tests 
def triangle(a, b, c) 
    t = Triangle.new(a, b, c) 
    return t.type 
end 

class Triangle 
    def initialize(a, b, c) 
    @sides = [a, b, c].sort 
    guard_against_invalid_lengths 
    end 

    def type 
    case @sides.uniq.size 
    when 1 then :equilateral 
    when 2 then :isosceles 
    else :scalene 
    end 
    end 

    private 
    def guard_against_invalid_lengths 
    if @sides.any? { |x| x <= 0 } 
     raise TriangleError, "Sides must be greater than 0" 
    end 

    if @sides[0] + @sides[1] <= @sides[2] 
     raise TriangleError, "Not valid triangle lengths"  
    end 
    end 
end 
+0

Đây là câu trả lời tuyệt vời cho phần Kiểm tra của about_triangle_project_2.rb. Tôi sao chép/pasta'd giải pháp của bạn mà đã được nhiều hơn nữa thanh lịch mà phiên bản mã máy của tôi. – TALLBOY

11

Một cách tiếp cận:

def triangle(a, b, c) 
    a, b, c = [a, b, c].sort 
    raise TriangleError if a <= 0 or a + b <= c 
    return :equilateral if a == c 
    return :isosceles if a == b or b == c 
    return :scalene 
end 
+0

+1 Sử dụng thông minh của đơn đặt hàng để tránh các so sánh không cần thiết. –

+0

Tôi phải thêm lớp TriangleError SteveO7

2

Hmm ..Tôi không biết về uniq - vì vậy đến từ Smalltalk (lứa tuổi trước) tôi đã sử dụng:

require 'set' 
def triangle(a, b, c) 
    case [a, b, c].to_set.count 
    when 1 then :equilateral 
    when 2 then :isosceles 
    else :scalene 
    end 
end 
+0

Tôi đã tìm một giải pháp thanh lịch hơn so với những gì tôi đã thực hiện và tìm thấy bài đăng này một năm rưỡi sau khi thực tế ... Nhưng tôi phải nói rằng tôi thực sự thích giải pháp này. Đó là một cách tiếp cận sạch sẽ tốt đẹp mà cũng rất dễ đọc; thanh danh! – bigtunacan

3

Đây là giải pháp của tôi:

def triangle(a, b, c) 
    sides = [a, b, c].sort 
    raise TriangleError, "Invalid side #{sides[0]}" unless sides[0] > 0 
    raise TriangleError, "Impossible triangle" if sides[0] + sides[1] <= sides[2] 
    return [:scalene, :isosceles, :equilateral][ 3 - sides.uniq.size ] 
end 
+0

Tôi thích điều này hoàn toàn thúc đẩy việc sắp xếp để đưa ra phản hồi lỗi chính xác hơn và ngắn nhất và dễ đọc nhất. Không cần sự trở lại mặc dù. – dansalmo

1

Đây là giải pháp của tôi:

def triangle(a, b, c) 
    return :equilateral if a == b and b == c 
    return :isosceles if (a == b or b == c or a == c) 
    return :scalene 
end 
5
class TriangleError < StandardError 
end 

def triangle(a, b, c) 
    sides = [a,b,c].sort 

    raise TriangleError if sides.first <= 0 || sides[2] >= sides[1] + sides[0] 
    return :equilateral if sides.uniq.length == 1 
    return :isosceles if sides.uniq.length == 2 
    :scalene 
end 
2

Xuất phát từ thế giới MATLAB, tôi quen với các chức năng mảng 'bất kỳ' và 'tất cả', và rất vui khi tìm thấy chúng trong Ruby. Vì vậy:

def triangle(a, b, c) 
    eqs = [a==b, a==c, b==c] 
    eqs.all?? :equilateral : eqs.any?? :isosceles : :scalene 
end 

Không biết liệu điều đó có tối ưu hay không, về khả năng đọc, thời gian tính toán ... (ruby noob).

Các vấn đề liên quan