Πότε να χρησιμοποιείτε λάμδα, πότε να χρησιμοποιήσετε Proc.new;

ψήφοι
316

Στο Ruby 1.8, υπάρχουν λεπτές διαφορές μεταξύ proc / λ από τη μία πλευρά, και Proc.newαπό την άλλη.

  • Ποιες είναι οι διαφορές;
  • Μπορεί να σας δώσει οδηγίες για το πώς να αποφασίσει ποια θα επιλέξει;
  • Στην Ruby 1.9, proc και λάμδα είναι διαφορετικά. Ποιο είναι το πρόβλημα?
Δημοσιεύθηκε 03/08/2008 στις 07:40
πηγή χρήστη
Σε άλλες γλώσσες...                            


15 απαντήσεις

ψήφοι
364

Μια άλλη σημαντική αλλά λεπτή διαφορά μεταξύ procs δημιουργηθεί με lambdaκαι procs που δημιουργήθηκαν με Proc.newτον τρόπο που χειρίζονται την returnδήλωση:

  • Σε μια lambda-Δημιουργήθηκε proc, η returnδήλωση επιστρέφει μόνο από το ίδιο το proc
  • Σε μια Proc.new-Δημιουργήθηκε proc, η returnδήλωση είναι λίγο πιο εκπληκτικό: επιστρέφει τον έλεγχο όχι μόνο από το proc, αλλά και από τη μέθοδο που περικλείει το proc!

Εδώ είναι lambda-Δημιουργήθηκε proc είναι returnσε δράση. Συμπεριφέρεται με τρόπο που ίσως περιμένατε:

def whowouldwin

  mylambda = lambda {return "Freddy"}
  mylambda.call

  # mylambda gets called and returns "Freddy", and execution
  # continues on the next line

  return "Jason"

end


whowouldwin
#=> "Jason"

Τώρα εδώ είναι μια Proc.new-Δημιουργήθηκε proc για returnνα κάνει το ίδιο πράγμα. Είστε έτοιμοι να δείτε μια από εκείνες τις περιπτώσεις όπου Ruby σπάει την πολυδιαφημισμένη αρχή της ελάχιστης Έκπληξη:

def whowouldwin2

  myproc = Proc.new {return "Freddy"}
  myproc.call

  # myproc gets called and returns "Freddy", 
  # but also returns control from whowhouldwin2!
  # The line below *never* gets executed.

  return "Jason"

end


whowouldwin2         
#=> "Freddy"

Χάρη σε αυτή την εκπληκτική συμπεριφορά (όπως και λιγότερη πληκτρολόγηση), έχω την τάση να ευνοούν τη χρήση lambdaπάνω από Proc.newόταν κάνετε procs.

Απαντήθηκε 03/08/2008 στις 16:21
πηγή χρήστη

ψήφοι
93

Για την παροχή περαιτέρω διευκρινίσεις:

Joey λέει ότι η συμπεριφορά επιστροφή του Proc.newείναι εκπληκτικό. Ωστόσο, αν σκεφτεί κανείς ότι Proc.new συμπεριφέρεται σαν ένα μπλοκ αυτό δεν αποτελεί έκπληξη το ότι είναι ακριβώς το πώς συμπεριφέρονται μπλοκ. Λαμπάς από την άλλη πλευρά συμπεριφέρονται περισσότερο σαν μεθόδους.

Αυτό εξηγεί γιατί στην πραγματικότητα procs είναι ευέλικτο, όταν πρόκειται για arity (σειρά επιχειρημάτων), ενώ λάμδα δεν είναι. Μπλοκάρει δεν απαιτούν όλα τα επιχειρήματά τους, που πρέπει να παρέχονται, αλλά οι μέθοδοι κάνουμε (εκτός εάν προβλέπεται προεπιλογή). Ενώ παρέχει προεπιλογή επιχείρημα λάμδα δεν είναι μια επιλογή σε Ruby 1.8, είναι πλέον υποστηρίζεται σε Ruby 1.9 με την εναλλακτική σύνταξη λάμδα (όπως σημειώνεται από webmat):

concat = ->(a, b=2){ "#{a}#{b}" }
concat.call(4,5) # => "45"
concat.call(1)   # => "12"

Και Michiel de Mare (ο ΕΠ) είναι εσφαλμένη για τα procs και λάμδα συμπεριφέρεται το ίδιο με arity σε Ruby 1.9. Έχω διαπιστώσει ότι εξακολουθούν να διατηρούν τη συμπεριφορά από 1,8, όπως ορίζεται παραπάνω.

breakκαταστάσεις δεν κάνουν πραγματικά πολύ νόημα είτε procs ή λάμδα. Σε procs, το διάλειμμα θα επιστρέψει από Proc.new η οποία έχει ήδη ολοκληρωθεί. Και δεν έχει κανένα νόημα να σπάσει από ένα λάμδα, δεδομένου ότι είναι ουσιαστικά μια μέθοδο, και ποτέ δεν θα σπάσει από το ανώτερο επίπεδο της μεθόδου.

next, redoΚαι raiseσυμπεριφέρονται το ίδιο σε δύο procs και λάμδα. Εκτιμώντας retryδεν επιτρέπεται ούτε και θα αυξήσει μια εξαίρεση.

Και τέλος, η procμέθοδος δεν πρέπει ποτέ να χρησιμοποιηθεί όπως είναι ασυνεπής και έχει απροσδόκητη συμπεριφορά. Στο Ruby 1.8 επιστρέφει στην πραγματικότητα ένα λάμδα! Σε Ruby 1.9 αυτό έχει καθοριστεί και επιστρέφει μια Proc. Αν θέλετε να δημιουργήσετε ένα Proc, να κολλήσει με Proc.new.

Για περισσότερες πληροφορίες, συστήνω ιδιαίτερα του O'Reilly Το Ruby γλώσσα προγραμματισμού που είναι η πηγή μου για τις περισσότερες από αυτές τις πληροφορίες.

Απαντήθηκε 04/10/2009 στις 06:23
πηγή χρήστη

ψήφοι
41

Βρήκα αυτή τη σελίδα που δείχνει ποια είναι η διαφορά μεταξύ Proc.newκαι lambdaείναι. Σύμφωνα με τη σελίδα, η μόνη διαφορά είναι ότι ένα λάμδα είναι αυστηρή για τον αριθμό των επιχειρημάτων που δέχεται, ενώ Proc.newμετατρέπει λείπουν τα επιχειρήματα για να nil. Εδώ είναι ένα παράδειγμα IRB συνεδρία που απεικονίζει τη διαφορά:

IRB (κύρια): 001: 0> l = λάμδα {| x, y | x + y}
=> # <Proc: 0x00007fc605ec0748 @ (IRB): 1>
IRB (κύρια): 002: 0> ρ = Proc.new {| x, y | x + y}
=> # <Proc: 0x00007fc605ea8698 @ (IRB): 2>
IRB (κύρια): 003: 0> l.call "γεια", "κόσμος"
=> "Helloworld"
IRB (κύρια): 004: 0> p.call "γεια", "κόσμος"
=> "Helloworld"
IRB (κύρια): 005: 0> l.call "γεια"
ArgumentError: λάθος σειρά επιχειρημάτων (1 2)
    από (IRB): 1
    από (IRB): 5: το `κλήση»
    από (IRB): 5
    από: 0
IRB (κύρια): 006: 0> p.call "γεια"
TypeError: Δεν μπορεί να μετατρέψει μηδέν σε String
    από (IRB): 2: το `+»
    από (IRB): 2
    από (IRB): 6: το `κλήση»
    από (IRB): 6
    από: 0

Η σελίδα συνιστά επίσης τη χρήση λάμδα αν θέλετε συγκεκριμένα το σφάλμα ανεκτική συμπεριφορά. Συμφωνώ με αυτό το συναίσθημα. Χρησιμοποιώντας ένα λάμδα φαίνεται ένα αγοράκι πιο περιεκτική, και με μια τέτοια ασήμαντη διαφορά, φαίνεται ότι η καλύτερη επιλογή στη μέση κατάσταση.

Όσο για Ruby 1.9, συγγνώμη, δεν έχω εξετάσει 1,9 ακόμα, αλλά δεν φαντάζομαι ότι θα το αλλάξει όλα αυτά πολύ (μην πάρτε τη λέξη μου για αυτό όμως, φαίνεται ότι έχετε ακούσει για κάποιες αλλαγές, έτσι είμαι μάλλον λάθος εκεί).

Απαντήθηκε 03/08/2008 στις 08:28
πηγή χρήστη

ψήφοι
14

Proc είναι μεγαλύτερα, αλλά η σημασιολογία της επιστροφής είναι πολύ αντιφατικό σε μένα (τουλάχιστον όταν ήμουν εκμάθηση της γλώσσας) διότι:

  1. Εάν χρησιμοποιείτε proc, που πιθανότατα χρησιμοποιούν κάποιο είδος λειτουργικού προτύπου.
  2. Proc μπορεί να επιστρέψει από το πεδίο εφαρμογής που περικλείει (βλέπε προηγούμενες απαντήσεις), η οποία είναι μια goto βασικά, και ιδιαίτερα μη-λειτουργικό χαρακτήρα.

Λ είναι λειτουργικά ασφαλέστερη και ευκολότερη λόγο για - το χρησιμοποιώ πάντα αντί για proc.

Απαντήθηκε 11/09/2008 στις 00:32
πηγή χρήστη

ψήφοι
11

Δεν μπορώ να πω πολλά για τις λεπτές διαφορές. Ωστόσο, μπορώ να επισημάνω ότι η Ruby 1.9 επιτρέπει τώρα προαιρετικές παραμέτρους για λάμδα και μπλοκ.

Εδώ είναι η νέα σύνταξη για τις stabby λάμδα κάτω από 1.9:

stabby = ->(msg='inside the stabby lambda') { puts msg }

Ruby 1.8 δεν έχουν αυτή τη σύνταξη. Ούτε το συμβατικό τρόπο δηλώνοντας μπλοκ / λάμδα υποστηρίξει προαιρετικό args:

# under 1.8
l = lambda { |msg = 'inside the stabby lambda'|  puts msg }
SyntaxError: compile error
(irb):1: syntax error, unexpected '=', expecting tCOLON2 or '[' or '.'
l = lambda { |msg = 'inside the stabby lambda'|  puts msg }

Ruby 1.9, ωστόσο, υποστηρίζει προαιρετικά ορίσματα, ακόμη και με το παλιό σύνταξη:

l = lambda { |msg = 'inside the regular lambda'|  puts msg }
#=> #<Proc:0x0e5dbc@(irb):1 (lambda)>
l.call
#=> inside the regular lambda
l.call('jeez')
#=> jeez

Αν θέλετε να χτίσετε Ruby1.9 για Leopard ή Linux, ελέγξτε έξω αυτό το άρθρο (ξεδιάντροπη αυτοπροβολή).

Απαντήθηκε 19/11/2008 στις 22:28
πηγή χρήστη

ψήφοι
10

Σύντομη απάντηση: Αυτό που έχει σημασία είναι τι returnκάνει: λ επιστρέφει από μόνη της, και proc επιστρέφει από τον εαυτό της και τη λειτουργία που ονομάζεται.

Αυτό που είναι λιγότερο σαφές είναι γιατί θέλετε να χρησιμοποιήσετε το καθένα. λ είναι αυτό που περιμένουμε τα πράγματα να κάνουμε σε μια λειτουργική έννοια προγραμματισμού. Πρόκειται ουσιαστικά για ένα ανώνυμο μέθοδος με την τρέχουσα πεδίο αυτόματα δεσμεύεται. Από τα δύο, λ είναι το ένα μάλλον θα πρέπει να χρησιμοποιούν.

Proc, από την άλλη πλευρά, είναι πολύ χρήσιμο για την εφαρμογή της ίδιας της γλώσσας. Για παράδειγμα, μπορείτε να εφαρμόσετε «εάν» δηλώσεις ή «για» βρόχους μαζί τους. Οποιαδήποτε επιστροφή βρέθηκαν στο proc θα επιστρέψει από τη μέθοδο που ονομάζεται, όχι το μόνο το «αν» δήλωση. Αυτό είναι το πώς λειτουργούν οι γλώσσες, πως «αν» εργάζονται καταστάσεις, έτσι ώστε η εικασία μου είναι Ruby χρησιμοποιεί αυτή κάτω από τα σκεπάσματα και το μόνο που θα εκτεθούν, γιατί φαινόταν ισχυρό.

Θα μόνο πραγματικά ανάγκη αυτό εάν θέλετε να δημιουργήσετε νέες γλωσσικές δομές, όπως θηλιές, αν-άλλος κατασκευές, κ.λπ.

Απαντήθηκε 06/10/2011 στις 19:33
πηγή χρήστη

ψήφοι
9

Ένας καλός τρόπος για να το δείτε είναι ότι οι λάμδα εκτελούνται στο δικό τους πεδίο εφαρμογής (σαν να ήταν μια κλήση μέθοδο), ενώ procs μπορεί να θεωρηθεί ως εκτελεστεί σε σειρά με τη μέθοδο κλήσης, τουλάχιστον αυτός είναι ένας καλός τρόπος για να κριθεί διασκορπισμένες σε κάποιον να χρησιμοποιήσει σε κάθε περίπτωση.

Απαντήθηκε 09/12/2008 στις 15:17
πηγή χρήστη

ψήφοι
8

Δεν το είχα προσέξει κάποιες παρατηρήσεις σχετικά με την τρίτη μέθοδο της queston, «proc», το οποίο έχει καταργηθεί, αλλά αντιμετωπίζονται με διαφορετικό τρόπο σε 1.8 και 1.9.

Εδώ είναι ένα αρκετά λεπτομερές παράδειγμα που το καθιστά εύκολο να δείτε τις διαφορές μεταξύ των τριών παρόμοιες κλήσεις:

def meth1
  puts "method start"

  pr = lambda { return }
  pr.call

  puts "method end"  
end

def meth2
  puts "method start"

  pr = Proc.new { return }
  pr.call

  puts "method end"  
end

def meth3
  puts "method start"

  pr = proc { return }
  pr.call

  puts "method end"  
end

puts "Using lambda"
meth1
puts "--------"
puts "using Proc.new"
meth2
puts "--------"
puts "using proc"
meth3
Απαντήθηκε 25/06/2009 στις 12:22
πηγή χρήστη

ψήφοι
7

Πώματα σε Ruby είναι μια καλή γενική εικόνα για το πώς μπλοκ, λάμδα και το έργο proc σε Ruby, με Ρουμπίνι.

Απαντήθηκε 28/08/2008 στις 14:50
πηγή χρήστη

ψήφοι
6

Κατανόηση Ruby μπλοκ, procs και λάμδα από τον Robert Sosinski εξηγεί με σαφήνεια αυτές τις έννοιες προγραμματισμού και ενισχύει τις εξηγήσεις παράδειγμα κώδικα. Αντικείμενα Μέθοδος σχετίζονται μεταξύ τους και καλύπτονται επίσης.

Απαντήθηκε 23/08/2013 στις 18:07
πηγή χρήστη

ψήφοι
5

λάμδα λειτουργεί όπως αναμένεται, όπως και σε άλλες γλώσσες.

Το ενσύρματο Proc.newείναι εκπληκτικό και σύγχυση.

Η returnδήλωση proc δημιουργήθηκε από Proc.newόχι μόνο θα επιστρέψει τον έλεγχο μόνο από την ίδια, αλλά και από τη μέθοδο που περικλείει αυτό .

def some_method
  myproc = Proc.new {return "End."}
  myproc.call

  # Any code below will not get executed!
  # ...
end

Μπορείτε να υποστηρίζουν ότι Proc.newεισάγει κώδικα στη μέθοδο που περικλείει, όπως ακριβώς και μπλοκ. Αλλά Proc.newδημιουργεί ένα αντικείμενο, ενώ μπλοκ είναι μέρος ενός αντικειμένου.

Και υπάρχει και μια άλλη διαφορά μεταξύ των λάμδα και Proc.new, η οποία είναι το χειρισμό τους (λάθος) επιχειρήματα. λάμδα παραπονιέται γι 'αυτό, ενώ Proc.newαγνοεί επιπλέον επιχειρήματα ή θεωρεί την απουσία επιχειρημάτων ως μηδέν.

irb(main):021:0> l = -> (x) { x.to_s }
=> #<Proc:0x8b63750@(irb):21 (lambda)>
irb(main):022:0> p = Proc.new { |x| x.to_s}
=> #<Proc:0x8b59494@(irb):22>
irb(main):025:0> l.call
ArgumentError: wrong number of arguments (0 for 1)
        from (irb):21:in `block in irb_binding'
        from (irb):25:in `call'
        from (irb):25
        from /usr/bin/irb:11:in `<main>'
irb(main):026:0> p.call
=> ""
irb(main):049:0> l.call 1, 2
ArgumentError: wrong number of arguments (2 for 1)
        from (irb):47:in `block in irb_binding'
        from (irb):49:in `call'
        from (irb):49
        from /usr/bin/irb:11:in `<main>'
irb(main):050:0> p.call 1, 2
=> "1"

BTW, procτο Ruby 1.8 δημιουργεί ένα λάμδα, ενώ σε Ruby 1.9+ συμπεριφέρεται σαν Proc.new, το οποίο είναι πραγματικά σύγχυση.

Απαντήθηκε 29/10/2014 στις 16:22
πηγή χρήστη

ψήφοι
3

Για να επεξεργαστεί σχετικά με την απάντηση Ακορντεόν Guy είναι:

Ανακοίνωση που Proc.newδημιουργεί ένα proc έξω από το να περάσει ένα μπλοκ. Πιστεύω ότι lambda {...}αναλύεται ως ένα είδος γραμματική, παρά μια κλήση μεθόδου η οποία περνά ένα μπλοκ. returnσης μέσα από ένα μπλοκ που συνδέονται με μια κλήση μεθόδου θα επιστρέψει από τη μέθοδο, δεν το μπλοκ, και η Proc.newυπόθεση είναι ένα παράδειγμα αυτού στο παιχνίδι.

(Αυτή είναι 1.8. Δεν ξέρω πώς αυτό μεταφράζεται σε 1.9.)

Απαντήθηκε 07/09/2008 στις 03:31
πηγή χρήστη

ψήφοι
2

Είμαι λίγο αργά για αυτό, αλλά υπάρχει μια μεγάλη, αλλά ελάχιστα γνωστή πράγμα για Proc.newνα μην αναφερθεί στα σχόλια σε όλα. Όπως με έγγραφα :

Proc::newμπορούν να κληθούν χωρίς μπλοκ μόνο εντός μιας μεθόδου με ένα συνημμένο μπλοκ, στην οποία περίπτωση ότι μπλοκ μετατρέπεται στοProc αντικείμενο.

Τούτου λεχθέντος, Proc.newσας δίνει τη δυνατότητα με τις μεθόδους απόδοσης της αλυσίδας:

def m1
  yield 'Finally!' if block_given?
end

def m2
  m1 &Proc.new
end

m2 { |e| puts e } 
#⇒ Finally!
Απαντήθηκε 28/04/2015 στις 13:15
πηγή χρήστη

ψήφοι
1

Αξίζει να υπογραμμιστεί ότι returnσε proc επιστρέφει από τη μέθοδο λεξιλογικά περικλείει, δηλαδή τη μέθοδο όπου δημιουργήθηκε το proc , όχι η μέθοδος που ονομάζεται proc. Αυτό είναι συνέπεια της ιδιοκτησίας κλείσιμο της procs. Έτσι ο κώδικας που ακολουθεί εξάγει τίποτα:

def foo
  proc = Proc.new{return}
  foobar(proc)
  puts 'foo'
end

def foobar(proc)
  proc.call
  puts 'foobar'
end

foo

Αν και η proc εκτελεί το foobar, δημιουργήθηκε το fooκαι έτσι οι returnέξοδοι foo, και όχι μόνο foobar. Όπως έγραψε ο Charles Caldwell παραπάνω, έχει ένα GOTO αίσθηση σε αυτό. Κατά τη γνώμη μου, returnείναι μια χαρά σε ένα μπλοκ που εκτελείται στο λεξιλογικό πλαίσιο, αλλά είναι πολύ λιγότερο διαισθητική, όταν χρησιμοποιείται σε ένα proc που εκτελείται σε ένα διαφορετικό πλαίσιο.

Απαντήθηκε 15/11/2018 στις 10:00
πηγή χρήστη

ψήφοι
1

Η διαφορά στη συμπεριφορά με returnείναι IMHO η πιο σημαντική διαφορά μεταξύ του 2. Προτιμώ επίσης λάμδα επειδή είναι λιγότερη πληκτρολόγηση από Proc.new :-)

Απαντήθηκε 11/08/2008 στις 03:09
πηγή χρήστη

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more