2009-04-13 49 views
7

Cần chuyển đổi mã sau từ Ruby thành C#. Tuy nhiên tôi hơi bối rối khi sử dụng từ khóa lợi nhuận và cú pháp chung của Ruby. bất kỳ ai biết một chút của Ruby nhỏ có thể vui lòng giúp đỡ và chuyển đổi mãChuyển đổi Ruby thành C#

class < < Cache 
STALE_REFRESH = 1 
STALE_CREATED = 2 

# Caches data received from a block 
# 
# The difference between this method and usual Cache.get 
# is following: this method caches data and allows user 
# to re-generate data when it is expired w/o running 
# data generation code more than once so dog-pile effect 
# won't bring our servers down 
# 
def smart_get(key, ttl = nil, generation_time = 30.seconds) 
    # Fallback to default caching approach if no ttl given 
    return get(key) { yield } unless ttl 

    # Create window for data refresh 
    real_ttl = ttl + generation_time * 2 
    stale_key = "#{key}.stale" 

    # Try to get data from memcache 
    value = get(key) 
    stale = get(stale_key) 

    # If stale key has expired, it is time to re-generate our data 
    unless stale 
    put(stale_key, STALE_REFRESH, generation_time) # lock 
    value = nil # force data re-generation 
    end 

    # If no data retrieved or data re-generation forced, re-generate data and reset stale key 
    unless value 
    value = yield 
    put(key, value, real_ttl) 
    put(stale_key, STALE_CREATED, ttl) # unlock 
    end 

    return value 
end 

cuối

Trả lời

12

Tôi không biết C# chút nào, vì vậy bất cứ điều gì tôi nói về C# nên được thực hiện với một hạt muối. Tuy nhiên, tôi sẽ cố gắng giải thích những gì diễn ra trong đoạn mã Ruby đó.

class << Cache 

Ruby có tên là phương thức singleton. Chúng không liên quan gì đến Mẫu thiết kế phần mềm Singleton, chúng chỉ là các phương thức được định nghĩa cho một và chỉ một đối tượng. Vì vậy, bạn có thể có hai trường hợp của cùng một lớp và thêm các phương thức vào một trong hai đối tượng đó.

Có hai cú pháp khác nhau cho phương thức singleton. Một là chỉ cần tiền tố tên của phương thức với đối tượng, vì vậy def foo.bar(baz) sẽ xác định phương thức bar chỉ cho đối tượng foo. Phương thức khác được gọi là mở lớp singleton và có vẻ giống như định nghĩa một lớp, bởi vì đó cũng là điều xảy ra ngữ nghĩa: các phương thức singleton thực sự sống trong một lớp vô hình được chèn giữa đối tượng và lớp thực tế của nó trong lớp hệ thống cấp bậc.

Cú pháp này trông giống như sau: class << foo. Điều này mở ra lớp đơn của đối tượng foo và mọi phương thức được xác định bên trong của thân lớp đó sẽ trở thành một phương thức đơn của đối tượng foo.

Tại sao điều này được sử dụng ở đây? Vâng, Ruby là một ngôn ngữ hướng đối tượng thuần túy, có nghĩa là mọi thứ, bao gồm cả các lớp là một đối tượng. Bây giờ, nếu các phương thức có thể được thêm vào các đối tượng riêng lẻ, và các lớp là các đối tượng, điều này có nghĩa là các phương thức có thể được thêm vào các lớp riêng lẻ. Nói cách khác, Ruby không cần sự phân biệt nhân tạo giữa các phương thức thông thường và các phương thức tĩnh (đó là một sự gian lận, dù sao đi nữa: chúng không thực sự là phương pháp, chỉ là các thủ tục được tôn vinh). Một phương thức tĩnh trong C# là gì, chỉ là một phương thức thông thường trong lớp đơn lớp của đối tượng lớp.

Tất cả điều này chỉ là một cách giải thích dài mà mọi thứ được xác định giữa class << Cache và tương ứng end của nó trở thành static.

STALE_REFRESH = 1 
    STALE_CREATED = 2 

Trong Ruby, mọi biến bắt đầu bằng chữ in hoa, thực sự là hằng số. Tuy nhiên, trong trường hợp này, chúng tôi sẽ không dịch các trường này thành các trường static const, mà là một trường enum, vì đó là cách chúng được sử dụng.

# Caches data received from a block 
    # 
    # The difference between this method and usual Cache.get 
    # is following: this method caches data and allows user 
    # to re-generate data when it is expired w/o running 
    # data generation code more than once so dog-pile effect 
    # won't bring our servers down 
    # 
    def smart_get(key, ttl = nil, generation_time = 30.seconds) 

Phương pháp này có ba thông số (bốn trên thực tế, chúng ta sẽ thấy chính xác lý do tại sao sau), hai trong số đó là tùy chọn (ttlgeneration_time). Cả hai đều có giá trị mặc định, tuy nhiên, trong trường hợp ttl giá trị mặc định không thực sự được sử dụng, nó phục vụ nhiều hơn như một điểm đánh dấu để tìm hiểu xem đối số có được chuyển vào hay không.

30.seconds là phần mở rộng mà thư viện ActiveSupport thêm vào lớp Integer. Nó không thực sự làm bất cứ điều gì, nó chỉ trả về self. Nó được sử dụng trong trường hợp này chỉ để làm cho định nghĩa phương thức dễ đọc hơn. (Có các phương pháp khác làm điều gì đó hữu ích hơn, ví dụ: Integer#minutes, trả về self * 60Integer#hours v.v.) Chúng tôi sẽ sử dụng điều này như một dấu hiệu cho thấy loại tham số không được là int mà là System.TimeSpan.

# Fallback to default caching approach if no ttl given 
    return get(key) { yield } unless ttl 

Điều này chứa một số cấu trúc phức tạp của Ruby. Hãy bắt đầu bằng cách đơn giản nhất: các công cụ sửa đổi có điều kiện. Nếu một phần tử có điều kiện chỉ chứa một biểu thức, thì điều kiện có thể được nối vào cuối biểu thức. Vì vậy, thay vì nói if a > b then foo end bạn cũng có thể nói foo if a > b. Vì vậy, ở trên tương đương với unless ttl then return get(key) { yield } end.

Điều tiếp theo cũng dễ dàng: unless chỉ là cú pháp đường cho if not. Vì vậy, chúng tôi hiện đang ở if not ttl then return get(key) { yield } end

Thứ ba là hệ thống sự thật của Ruby. Trong Ruby, sự thật là khá đơn giản. Trên thực tế, sự sai lệch khá đơn giản và sự thật rơi tự nhiên: từ khóa đặc biệt false là sai và từ khóa đặc biệt nil là sai, mọi thứ khác đều đúng. Vì vậy, trong trường hợp này, điều kiện sẽ chỉ chỉ là đúng, nếu ttlfalse hoặc nil. false không phải là một giá trị hợp lý khủng khiếp cho một khoảng thời gian, do đó, chỉ có một thú vị là nil. Đoạn mã sẽ được viết rõ ràng hơn như sau: if ttl.nil? then return get(key) { yield } end. Vì giá trị mặc định cho thông số ttlnil, điều kiện này là đúng, nếu không có đối số nào được chuyển cho ttl. Vì vậy, điều kiện được sử dụng để tìm ra có bao nhiêu đối số mà phương thức được gọi, có nghĩa là chúng ta sẽ không dịch nó như là một điều kiện quá tải mà là một phương thức quá tải.

Hiện tại, trên yield. Trong Ruby, mọi phương thức đều có thể chấp nhận một khối mã ngầm định như một đối số. Đó là lý do tại sao tôi đã viết ở trên rằng phương pháp thực sự mất bốn đối số, không phải ba. Một khối mã chỉ là một đoạn mã ẩn danh có thể được truyền xung quanh, được lưu trữ trong một biến và được gọi sau này. Ruby kế thừa các khối từ Smalltalk, nhưng khái niệm này có từ năm 1958, đến các biểu thức lambda của Lisp. Khi đề cập đến các khối mã ẩn danh, nhưng ít nhất là bây giờ, khi đề cập đến các biểu thức lambda, bạn nên biết cách đại diện cho tham số phương thức thứ tư tiềm ẩn này: một loại đại biểu, cụ thể hơn là một Func.

Vì vậy, yield làm gì? Nó chuyển quyền kiểm soát khối. Về cơ bản nó chỉ là một cách rất thuận tiện để gọi một khối, mà không cần phải lưu trữ nó một cách rõ ràng trong một biến và sau đó gọi nó.

# Create window for data refresh 
    real_ttl = ttl + generation_time * 2 
    stale_key = "#{key}.stale" 

cú pháp #{foo} này được gọi là chuỗi suy. Nó có nghĩa là "thay thế mã thông báo bên trong chuỗi với bất kỳ kết quả nào của việc đánh giá biểu thức giữa các dấu ngoặc nhọn". Nó chỉ là một phiên bản rất ngắn gọn của String.Format(), đó chính xác là những gì chúng tôi sẽ dịch nó sang.

# Try to get data from memcache 
    value = get(key) 
    stale = get(stale_key) 

    # If stale key has expired, it is time to re-generate our data 
    unless stale 
     put(stale_key, STALE_REFRESH, generation_time) # lock 
     value = nil # force data re-generation 
    end 

    # If no data retrieved or data re-generation forced, re-generate data and reset stale key 
    unless value 
     value = yield 
     put(key, value, real_ttl) 
     put(stale_key, STALE_CREATED, ttl) # unlock 
    end 

    return value 
    end 
end 

Đây là nỗ lực yếu ớt của tôi tại dịch phiên bản Ruby để C#:

public class Cache<Tkey, Tvalue> { 
    enum Stale { Refresh, Created } 

    /* Caches data received from a delegate 
    * 
    * The difference between this method and usual Cache.get 
    * is following: this method caches data and allows user 
    * to re-generate data when it is expired w/o running 
    * data generation code more than once so dog-pile effect 
    * won't bring our servers down 
    */ 
    public static Tvalue SmartGet(Tkey key, TimeSpan ttl, TimeSpan generationTime, Func<Tvalue> strategy) 
    { 
     // Create window for data refresh 
     var realTtl = ttl + generationTime * 2; 
     var staleKey = String.Format("{0}stale", key); 

     // Try to get data from memcache 
     var value = Get(key); 
     var stale = Get(staleKey); 

     // If stale key has expired, it is time to re-generate our data 
     if (stale == null) 
     { 
      Put(staleKey, Stale.Refresh, generationTime); // lock 
      value = null; // force data re-generation 
     } 

     // If no data retrieved or data re-generation forced, re-generate data and reset stale key 
     if (value == null) 
     { 
      value = strategy(); 
      Put(key, value, realTtl); 
      Put(staleKey, Stale.Created, ttl) // unlock 
     } 

     return value; 
    } 

    // Fallback to default caching approach if no ttl given 
    public static Tvalue SmartGet(Tkey key, Func<Tvalue> strategy) => 
     Get(key, strategy); 

    // Simulate default argument for generationTime 
    // C# 4.0 has default arguments, so this wouldn't be needed. 
    public static Tvalue SmartGet(Tkey key, TimeSpan ttl, Func<Tvalue> strategy) => 
     SmartGet(key, ttl, new TimeSpan(0, 0, 30), strategy); 

    // Convenience overloads to allow calling it the same way as 
    // in Ruby, by just passing in the timespans as integers in 
    // seconds. 
    public static Tvalue SmartGet(Tkey key, int ttl, int generationTime, Func<Tvalue> strategy) => 
     SmartGet(key, new TimeSpan(0, 0, ttl), new TimeSpan(0, 0, generationTime), strategy); 

    public static Tvalue SmartGet(Tkey key, int ttl, Func<Tvalue> strategy) => 
     SmartGet(key, new TimeSpan(0, 0, ttl), strategy); 
} 

Xin lưu ý rằng tôi không biết C#, tôi không biết NET, tôi đã không kiểm tra này, tôi thậm chí không biết nó có hợp lệ hay không. Hy vọng nó sẽ giúp anyway.

+0

@ Jorg - bạn sẽ giúp tôi nếu tôi đăng một mã cho Ruby để chuyển đổi C# ??? – maliks

+0

câu trả lời hoàn hảo: giải thích tốt + mã – trinalbadger587

5

Nó xuất hiện mã này đang được thông qua một khối được đánh giá nếu bộ nhớ cache không chứa các dữ liệu yêu cầu (yield là cách bạn gọi khối). Đây là mã ruby ​​khá thành ngữ; Tôi không biết làm thế nào (hoặc thậm chí nếu) bạn có thể "dịch nó" để C#.

Tìm trường hợp sử dụng để xem ý tôi là gì. Bạn nên tìm một cái gì đó mơ hồ như thế này:

x = smart_get([:foo,"bar"]) { call_expensive_operation_foo("bar") } 

Một cược tốt hơn là nên tìm ra những gì bạn cần phải làm gì và viết một cái gì đó mà không có de novo trong C#, chứ không phải cố gắng để "dịch" từ ruby.

+0

Người ta có thể làm điều gì đó tương tự với các biểu thức lambda (xem http://msdn.microsoft.com/en-us/library/bb397687.aspx) trong .NET> = 3.0. –

+0

Bạn có thể, nhưng dịch mã thành ngữ rất nhiều từ ngôn ngữ này sang ngôn ngữ khác không bao giờ hoạt động cũng như bạn muốn. Nó luôn luôn có vẻ như bật ra loại giống như tiếng cười gõ tiếng Nhật. – MarkusQ

4

Dường như bạn đang cố gắng chuyển cổng memcache-client từ Ruby sang C#. Nếu vậy, nó có thể được dễ dàng hơn để sử dụng một C# memcache thực hiện khách hàng bản địa như:

http://code.google.com/p/beitmemcached/

Dù bằng cách nào, tôi thường đồng ý với MarkusQ rằng dịch từ một ngôn ngữ cấp cao sang ngôn ngữ cấp thấp hơn là có lẽ sẽ ít hiệu quả hơn so với việc viết lại theo kiểu thành ngữ cho ngôn ngữ đích. Một bản dịch thẳng từ Ruby sang C# sẽ cung cấp cho bạn một số mã rất xấu, tốt nhất.

Từ khóa Ruby cho phép bạn gọi một khối mã đã được chuyển như một đối số được khai báo ngầm định cho phương thức. Vì vậy, ví dụ, bạn có thể nghĩ về định nghĩa phương pháp smart_get như thực sự tìm kiếm nhiều hơn như:

def smart_get(key, ttl = nil, generation_time = 30.seconds, &block) 

Và khi bạn gọi smart_get như vậy:

x = smart_get("mykey", my_ttl) { do_some_operation_here } 

Các công cụ trong niềng răng được gán cho khối biến trong định nghĩa mở rộng ở trên. năng suất sau đó gọi mã trong khối &. Đây là một đơn giản hóa tổng, nhưng sẽ giúp bạn có được ý tưởng chung.

Quay lại chuyển đổi của bạn. Sự đơn giản hóa mà tôi đã làm không nhất thiết phải giúp bạn đạt được 100% ở đó, bởi vì ngay khi bạn tìm thấy cách C# để dịch mã đó, một đoạn mã khác sẽ phá vỡ bản dịch của bạn. Ví dụ, giả sử một phương pháp cho nhu cầu để kiểm tra các khối:

def foo 
    if block.arity == 0 
     # No arguments passed, load defaults from config file and ignore call 
    else 
     yield 
    end 
end 

Và dĩ nhiên kể từ lambdas là đối tượng hạng nhất trong Ruby, bạn có thể gặp phải mã như sau:

foo = lambda { |a, b, c| a + b + c } 
# foo is now defined as a function that sums its three arguments 

Và sau đó Thiên Chúa giúp bạn nếu bạn gặp mã xác định phương pháp trên bay, hoặc (tồi tệ hơn cho một dịch giả) có lợi thế của các lớp dễ uốn của Ruby:

class Foo 
    def show 
     puts "Foo" 
    end 
end 

foo = Foo.new 
foo.show # prints "Foo" 

class <&lt;foo; def show; puts "Bar"; end; end 

foo.show # prints "Bar" 

Foo.new.show # prints "Foo" 

foo.show # Still prints "Bar" 

Vì mỗi trường hợp o f mỗi lớp trong Ruby có thể có định nghĩa lớp riêng của nó, tôi không thực sự chắc chắn làm thế nào bạn sẽ cổng ngay cả ví dụ đơn giản để C# mà không có thể dục dụng cụ ưa thích.Ruby có rất nhiều tính năng này sẽ phá vỡ một nỗ lực dịch thuật đơn giản, vì vậy tôi khuyên bạn nên học điều bạn đang cố gắng chuyển, sau đó làm lại nó từ đầu.

0

Hãy thử điều này:

def foo 
    if block.arity == 0 
     # No arguments passed, load defaults from config file and ignore call 
    else 
     yield 
    end 
end 
Các vấn đề liên quan