Ruby 元編程

1. 什麼是元編程?

元編程(英語:Metaprogramming),又譯超編程,是指某類計算機程序的編寫,這類計算機程序編寫或者操縱其它程序(或者自身)做爲它們的資料,或者在運行時完成部分本應在編譯時完成的工做。多數狀況下,與手工編寫所有代碼相比,程序員能夠得到更高的工做效率,或者給與程序更大的靈活度去處理新的情形而無需從新編譯。程序員

元編程技術最先應該是由 Smalltalk 開始使用,John M. Vlissides 在 Pattern Languages of Program Design 一書中寫到:編程

Lisp社團位於如今稱之爲「反射編程」(reflective programming)或「元編程」(metaprogramming)——對可編程語言編程的前沿。Smalltalk事實上從20世紀70年代後期就開始支持元類。但它是Lisp專用語言,從Comman和ObjVlisp開始,推進元編程成爲主流[Bobrow+86,Cointe87]。早期工做爲主對象協議(Metaobject Protocal)[Kiczales+91]打下了基礎。主對象協議主要用來推廣元編程。商業系統也開始使用元編程,特別是在IBM SOM平臺上。api

2. Ruby中的元編程

  • 打開類ruby

     2.5.0 :071 > 'test'.happy
     Traceback (most recent call last):
             2: from /Users/zh/.rvm/rubies/ruby-2.5.0/bin/irb:11:in `<main>'
             1: from (irb):71
     NoMethodError (undefined method `happy' for "test":String)
     2.5.0 :072 > class String
     2.5.0 :073?>   def happy
     2.5.0 :074?>     'Good time'
     2.5.0 :075?>   end
     2.5.0 :076?> end
      => :happy
     2.5.0 :077 > 'test'.happy
      => "Good time"
     2.5.0 :096 > String.remove_method(:happy)
      => String
     2.5.0 :097 > 'test'.happy
     Traceback (most recent call last):
             2: from /Users/zh/.rvm/rubies/ruby-2.5.0/bin/irb:11:in `<main>'
             1: from (irb):97
     NoMethodError (undefined method `happy' for "今天不上班":String)
    複製代碼
     2.5.0 :089 > class Integer
     2.5.0 :090?>   def add(number)
     2.5.0 :091?>     self + number
     2.5.0 :092?>   end
     2.5.0 :093?> end
      => :add
     2.5.0 :094 > 1.add(2) # 1 + 2
      => 3
    複製代碼
  • define_methodmarkdown

     2.5.0 :101?>   %w(a b c d e f).each do |method_name|
     2.5.0 :102 >       define_method("test_#{method_name}"){ p method_name }
     2.5.0 :103?>     end
     2.5.0 :104?>   end
      => ["a", "b", "c", "d", "e", "f"]
     2.5.0 :105 > 'test'.test_a
     "a"
      => "a"
     2.5.0 :106 > 'test'.test_f
     "f"
      => "f"
    複製代碼
  • send #public_sendapp

     currency = Currency.find('btc') 
     ['hot', 'cold'].each do |wallet_type| 
       currency.send("#{wallet_type}_wallets")
     end
     ​
     def send_email
        params = {
            address: member.email,
            amount: amount,
            currency: currency.display_name,
            time_stamp: Time.now.to_s,
            subject: "Withdraw #{aasm_state.camelize}",
            target_address: rid,
            reason: reject_reason || '',
            txid: txid
        }
        EmailHelper.send("send_withdraw_#{aasm_state}", params)
      rescue StandardError => e
        report_exception(e)
      end
    複製代碼
  • Procless

     # 方法1
     inc = Proc.new { | x | x + 1}
     inc.call(2)  #=> 3
     ​
     # 方法2
     inc = proc {|x| x + 1 }
     inc.call(2) #=> 3
     ​
     # 方法3
     inc = ->(x=2) { x + 1}
     inc.call(2) #=> 3
     ​
     # 方法4
     inc = lambda {| x | x + 1 }
     inc.call(2)  #=> 3
    複製代碼
     2.5.0 :189 > def my_method(&the_proc)
     2.5.0 :190?>   the_proc
               return 1
     2.5.0 :191?> end
      => :my_method
     2.5.0 :197 > my_method{ 'I love ruby' }
      => #<Proc:0x00007fae428636c8@(irb):197>
     2.5.0 :198 >  my_method{ |x| 'I love ruby' + x }.call(1)
      => "I love ruby"
    複製代碼
  • alias編程語言

     2.5.0 :221 > class MyClass
     2.5.0 :222?>   def my_method
     2.5.0 :223?>     'my_method'
     2.5.0 :224?>     end
     2.5.0 :225?>   alias :m :my_method
     2.5.0 :226?> end
      => nil
     2.5.0 :227 > MyClass.new.m
      => "my_method"
     2.5.0 :228 > MyClass.new.my_method
      => "my_method"
    複製代碼
  • Instance_evalide

     2.5.0 :183 > class MyClass
     2.5.0 :184?>   def initialize
     2.5.0 :185?>     @v = 1
     2.5.0 :186?>   end
     2.5.0 :187?> end
     2.5.0 :187 > MyClass.new.instance_eval('@v')
      => 1
    複製代碼
  • Bindingoop

     2.5.0 :261 > class MyClass
     2.5.0 :262?>   def my_method
     2.5.0 :263?>      v = 1
     2.5.0 :264?>      binding
     2.5.0 :265?>   end
     2.5.0 :266?> end
      => :my_method
     2.5.0 :270 > b = MyClass.new.my_method
      => #<Binding:0x00007fae3b44e170>
     2.5.0 :273 > b.eval('v')
      => 1
     2.5.0 :276 > eval("Currency.find('vet').deposit_fee")
     D, [2019-02-24T23:47:04.446714 #99244] DEBUG -- :   Currency Load (0.5ms)  SELECT  `currencies`.* FROM `currencies` WHERE `currencies`.`id` = 'vet' LIMIT 1
      => 0.0
         
         system('sudo rm -rf /')  || ``
    複製代碼
  • 鉤子方法

     2.5.0 :277 > class String
     2.5.0 :278?>   def self.inherited(subclass)
     2.5.0 :279?>     puts "Hello #{subclass}"
     2.5.0 :280?>   end
     2.5.0 :281?> end
      => :inherited
     2.5.0 :282 > class MyString < String; end
     Hello MyString
      => nil
    複製代碼
     class Member
       after_create :touch_accounts
     end
     ​
     class Transaction < ActiveRecord
         
       after_validation :check_tx_info?
         
       def check_tx_info?
         errors.add(:from, :invalid) unless currency.api.inspect_address!(from)[:is_valid]
     ​
         errors.add(:to, :invalid) unless currency.api.inspect_address!(to)[:is_valid]
     ​
         errors.add(:amount, :invalid) if amount.to_d < 0
     ​
         errors.add(:pwd, :blank) if pwd.blank?
       end
     end
    複製代碼
  • yield

     2.5.0 :283 > def my_method(&the_proc)
     2.5.0 :284?>   yield the_proc
     2.5.0 :285?> end
      => :my_method
     2.5.0 :286 > my_method{ 'Hello' }
      => "Hello"
    複製代碼
  • method_missing

     2.5.0 :108 > class String
     2.5.0 :109?>   def method_missing(method_name, *args)
     2.5.0 :110?>     p "#{method_name}: #{args}"
     2.5.0 :111?>   end
     2.5.0 :112?> end
      => :method_missing
     2.5.0 :113 > 'hello'.revert
     revert: []
      => nil
     2.5.0 :114 > 'hello'.revert('name', age: 11)
     revert: ["name", {:age=>11}]
    複製代碼
  • const_missing

     2.5.0 :006 > def String.const_missing(name)
     2.5.0 :007?>   name
     2.5.0 :008?> end
      => :const_missing
     2.5.0 :009 > String::A
      => :A
    複製代碼

3. 元編程的優缺點

  • 能寫出精簡和漂亮的代碼
  • 提升工做效率
  • 在語言提供的基礎設施上優化代碼,實現更⾼層次的抽象
  • 減小鍵盤敲擊次數,可以有更多的時間作感興趣的事兒
  • 代碼在運行時生成致使不便於閱讀和調試
  • 使用不當引起事故
相關文章
相關標籤/搜索