Parameter opsional Ruby

121

Jika saya mendefinisikan fungsi Ruby seperti ini:

def ldap_get ( base_dn, filter, scope=LDAP::LDAP_SCOPE_SUBTREE, attrs=nil )

Bagaimana saya bisa menyebutnya hanya memasok 2 argumen pertama dan argumen terakhir? Kenapa tidak seperti itu

ldap_get( base_dn, filter, , X)

mungkin atau jika memungkinkan, bagaimana cara melakukannya?

Bruno Antunes
sumber

Jawaban:

131

Ini tidak mungkin dengan ruby ​​saat ini. Anda tidak dapat meneruskan atribut 'kosong' ke metode. Yang paling dekat yang bisa Anda dapatkan adalah melewati nihil:

ldap_get(base_dn, filter, nil, X)

Namun, ini akan menyetel cakupan ke nihil, bukan LDAP :: LDAP_SCOPE_SUBTREE.

Apa yang dapat Anda lakukan adalah menyetel nilai default dalam metode Anda:

def ldap_get(base_dn, filter, scope = nil, attrs = nil)
  scope ||= LDAP::LDAP_SCOPE_SUBTREE
  ... do something ...
end

Sekarang jika Anda memanggil metode seperti di atas, perilakunya akan seperti yang Anda harapkan.

tomafro
sumber
21
Sedikit gotcha dengan metode ini: mis. Jika Anda mencoba membuat nilai default menjadi scopebenar dan Anda meneruskan false, scope ||= truetidak akan berfungsi. Ini mengevaluasi sama nildan akan mengaturnya ketrue
Joshua Pinter
4
apakah mungkin dengan ruby ​​versi saat ini, 3 tahun setelah jawaban ini?
dalloliogm
1
@JoshPinter, penjelasan yang bagus. Pada dasarnya || = bukan a = b atau c, aku ngeri saat melihat xyz||=true. Ini mengatakan jika nihil, itu selalu benar. Jika itu benar, itu benar.
Damon Aw
7
Dengan semua orang mengatakan betapa buruknya scope ||= true, saya terkejut bahwa tidak ada yang menyebutkan bahwa cara yang lebih baik untuk melakukan ini adalah dengan scope = LDAP::LDAP_SCOPE_SUBTREE if scope.nil?. Tentu saja, itu pun dengan asumsi bahwa itu niladalah nilai yang tidak valid.
Erik Sandberg
1
Perbarui ke oldie ini: Alternatifnya adalah dengan menggunakan notasi garis bawah. Sayangnya, ini memiliki efek yang sama dengan menyetel parameter ke nil. Beberapa mungkin menyukai notasi: ldap_get(base_dn, filter, _, X)(Catatan: Saya belum tahu (belum) kapan ini diperkenalkan di Ruby. Benang SO yang menarik ).
Eric Platon
137

Anda hampir selalu lebih baik menggunakan hash opsi.

def ldap_get(base_dn, filter, options = {})
  options[:scope] ||= LDAP::LDAP_SCOPE_SUBTREE
  ...
end

ldap_get(base_dn, filter, :attrs => X)
jshen
sumber
23
Strategi umum adalah memiliki hash opsi default dan menggabungkan apa pun yang diteruskan:options = default_options.merge(options)
Nathan Long
7
Saya tidak menyarankan ini karena opsi tidak memberi tahu Anda apa yang diharapkan metode atau nilai default-nya
Bron Davies
53

Waktu telah berlalu dan sejak versi 2 Ruby mendukung parameter bernama:

def ldap_get ( base_dn, filter, scope: "some_scope", attrs: nil )
  p attrs
end

ldap_get("first_arg", "second_arg", attrs: "attr1, attr2") # => "attr1, attr2"
steenslag.dll
sumber
1
Anda juga dapat menggunakan gambar percikan ganda untuk mengumpulkan argumen kata kunci tambahan yang tidak ditentukan. Ini terkait dengan masalah ini: stackoverflow.com/a/35259850/160363
Henry Tseng
3

Tidak mungkin melakukannya dengan cara yang Anda tentukan ldap_get. Namun, jika Anda mendefinisikan ldap_getseperti ini:

def ldap_get ( base_dn, filter, attrs=nil, scope=LDAP::LDAP_SCOPE_SUBTREE )

Sekarang kamu bisa:

ldap_get( base_dn, filter, X )

Tetapi sekarang Anda memiliki masalah yang tidak dapat Anda sebut dengan dua argumen pertama dan argumen terakhir (masalah yang sama seperti sebelumnya tetapi sekarang argumen terakhir berbeda).

Alasannya sederhana: Setiap argumen di Ruby tidak diharuskan memiliki nilai default, jadi Anda tidak bisa menyebutnya seperti yang Anda tentukan. Dalam kasus Anda, misalnya, dua argumen pertama tidak memiliki nilai default.

Chris Bunch
sumber
1

1) Anda tidak dapat membebani metode ( Mengapa ruby ​​tidak mendukung metode kelebihan beban? ) Jadi mengapa tidak menulis metode baru sama sekali?

2) Saya memecahkan masalah serupa menggunakan operator percikan * untuk array dengan panjang nol atau lebih. Kemudian, jika saya ingin mengirimkan parameter yang saya bisa, itu diartikan sebagai array, tetapi jika saya ingin memanggil metode tanpa parameter apa pun maka saya tidak perlu melewatkan apa pun. Lihat Bahasa Pemrograman Ruby halaman 186/187

rupweb
sumber
0

Baru-baru ini saya menemukan jalan keluarnya. Saya ingin membuat metode di kelas array dengan parameter opsional, untuk menyimpan atau membuang elemen dalam array.

Cara saya mensimulasikan ini adalah dengan melewatkan array sebagai parameter, dan kemudian memeriksa apakah nilai pada indeks itu nihil atau tidak.

class Array
  def ascii_to_text(params)
    param_len = params.length
    if param_len > 3 or param_len < 2 then raise "Invalid number of arguments #{param_len} for 2 || 3." end
    bottom  = params[0]
    top     = params[1]
    keep    = params[2]
    if keep.nil? == false
      if keep == 1
        self.map{|x| if x >= bottom and x <= top then x = x.chr else x = x.to_s end}
      else
        raise "Invalid option #{keep} at argument position 3 in #{p params}, must be 1 or nil"
      end
    else
      self.map{|x| if x >= bottom and x <= top then x = x.chr end}.compact
    end
  end
end

Mencoba metode kelas kami dengan parameter berbeda:

array = [1, 2, 97, 98, 99]
p array.ascii_to_text([32, 126, 1]) # Convert all ASCII values of 32-126 to their chr value otherwise keep it the same (That's what the optional 1 is for)

keluaran: ["1", "2", "a", "b", "c"]

Oke, keren itu berfungsi sesuai rencana. Sekarang mari kita periksa dan lihat apa yang terjadi jika kita tidak meneruskan opsi parameter ketiga (1) dalam array.

array = [1, 2, 97, 98, 99]
p array.ascii_to_text([32, 126]) # Convert all ASCII values of 32-126 to their chr value else remove it (1 isn't a parameter option)

keluaran: ["a", "b", "c"]

Seperti yang Anda lihat, opsi ketiga dalam larik telah dihapus, sehingga memulai bagian yang berbeda dalam metode dan menghapus semua nilai ASCII yang tidak berada dalam jangkauan kami (32-126)

Alternatifnya, kita bisa mengeluarkan nilai nihil dalam parameter. Yang akan terlihat mirip dengan blok kode berikut:

def ascii_to_text(top, bottom, keep = nil)
  if keep.nil?
    self.map{|x| if x >= bottom and x <= top then x = x.chr end}.compact
  else
    self.map{|x| if x >= bottom and x <= top then x = x.chr else x = x.to_s end}
end
Keganjilan
sumber
-1

Mungkin saja :) Ubah saja definisi

def ldap_get ( base_dn, filter, scope=LDAP::LDAP_SCOPE_SUBTREE, attrs=nil )

untuk

def ldap_get ( base_dn, filter, *param_array, attrs=nil )
scope = param_array.first || LDAP::LDAP_SCOPE_SUBTREE

scope sekarang akan berada dalam array di tempat pertama. Ketika Anda memberikan 3 argumen, maka Anda akan menetapkan base_dn, filter dan attrs dan param_array akan menjadi [] Ketika 4 dan lebih banyak argumen maka param_array akan menjadi [argument1, or_more, and_more]

Kelemahannya adalah ... ini adalah solusi yang tidak jelas, sangat jelek. Ini untuk menjawab bahwa dimungkinkan untuk menghilangkan argumen di tengah pemanggilan fungsi di ruby ​​:)

Hal lain yang harus Anda lakukan adalah menulis ulang nilai default dari scope.

m4risU
sumber
4
Solusi ini sepenuhnya salah. Ini adalah kesalahan sintaks untuk menggunakan parameter nilai default ( attrs=nil) setelah splat ( *param_array).
Erik Sandberg
3
-1: Erik benar. Menyebabkan kesalahan sintaks di irb 2.0.0p247. Menurut The Ruby Programming Language , di Ruby 1.8, parameter percikan harus terakhir kecuali untuk &parameter, tetapi di Ruby 1.9 bisa diikuti oleh "parameter biasa" juga. Dalam kedua kasus tersebut tidak ada parameter dengan hukum default setelah parameter dengan percikan.
andyg0808
Bahasa Pemrograman Ruby halaman 186/187 percikan ini baik-baik saja untuk digunakan dengan metode. Ini harus menjadi parameter terakhir dalam metode kecuali & digunakan.
rupweb
Jadi AndyG benar, urutannya harus: def ldap_get (base_dn, filter, attrs = nil, * param_array)
rupweb
-1

Anda dapat melakukan ini dengan aplikasi parsial, meskipun menggunakan variabel bernama pasti menghasilkan kode yang lebih mudah dibaca. John Resig menulis artikel blog pada tahun 2008 tentang cara melakukannya di JavaScript: http://ejohn.org/blog/p Partial-functions-in-javascript/

Function.prototype.partial = function(){
  var fn = this, args = Array.prototype.slice.call(arguments);
  return function(){
    var arg = 0;
    for ( var i = 0; i < args.length && arg < arguments.length; i++ )
      if ( args[i] === undefined )
        args[i] = arguments[arg++];
    return fn.apply(this, args);
  };
};

Mungkin akan memungkinkan untuk menerapkan prinsip yang sama di Ruby (kecuali untuk pewarisan prototipe).

EriF89
sumber