6 Stimmen

Einige einfache Ruby-Fragen - Iteratoren, Blöcke und Symbole

Mein Hintergrund ist PHP und C#, aber ich würde gerne RoR lernen. Zu diesem Zweck habe ich begonnen, die offizielle Dokumentation zu lesen. Ich habe einige Fragen zu einigen Codebeispielen.

Die erste ist mit Iteratoren:

class Array
  def inject(n)
    each { |value| n = yield(n, value) }
    n
  end

  def sum
    inject(0) { |n, value| n + value }
  end

  def product
    inject(1) { |n, value| n * value }
  end
end

Ich verstehe, dass yield bedeutet "führe den zugehörigen Block hier aus". Was mich verwirrt, ist die |value| n = Teil des each . Die anderen Blöcke machen mehr Sinn für mich, da sie scheinen zu imitieren C# Stil Lambdas:

public int sum(int n, int value)
{
    return Inject((n, value) => n + value);
}

Aber das erste Beispiel ist für mich verwirrend.

Die andere ist mit Symbolen. Wann würde ich sie verwenden wollen? Und warum kann ich nicht etwas tun wie:

class Example
  attr_reader @member

  # more code
end

3voto

Matheus Moreira Punkte 16610

En el inject o reduce Methode, n stellt einen akkumulierten Wert dar, d. h. das Ergebnis jeder Iteration wird in der n variabel. Dies könnte, wie in Ihrem Beispiel, die Summe oder das Produkt der Elemente im Array sein.

yield gibt das Ergebnis des Blocks zurück, das in n und in den nächsten Iterationen verwendet. Dadurch wird das Ergebnis "kumulativ".

a = [ 1, 2, 3 ]
a.sum  # inject(0) { |n, v| n + v }
# n == 0; n = 0 + 1
# n == 1; n = 1 + 2
# n == 3; n = 3 + 3
 => 6

Um die Summe zu berechnen, hätten Sie auch schreiben können a.reduce :+ . Dies funktioniert für jede binäre Operation. Wenn Ihre Methode den Namen symbol Schreiben a.reduce :symbol ist dasselbe wie das Schreiben a.reduce { |n, v| n.symbol v } .

attr und Unternehmen sind eigentlich Methoden. Unter der Haube definieren sie die Methoden dynamisch für Sie. Es verwendet das von Ihnen übergebene Symbol, um die Namen der Instanzvariablen und der Methoden zu ermitteln. :member ergibt sich im @member Instanzvariable und die member y member = Methoden.

Der Grund, warum Sie nicht schreiben können attr_reader @member ist, weil @member ist weder ein Objekt an sich, noch kann es in ein Symbol umgewandelt werden; es weist Ruby an, den Wert der Instanzvariablen @member der self Objekt, das auf Klassenebene die Klasse selbst ist.

Zur Veranschaulichung:

class Example
  @member = :member
  attr_accessor @member
end

e = Example.new
e.member = :value
e.member
 => :value

Denken Sie daran, dass der Zugriff auf nicht gesetzte Instanzvariablen zu nil und da die attr Methode Familie akzeptiert nur Symbole, Sie erhalten: TypeError: nil is not a symbol .

Betreffend Symbol Nutzung, können Sie gewissermaßen sie wie Schnüre verwenden. Sie eignen sich hervorragend als Hash-Schlüssel, da sich gleiche Symbole im Gegensatz zu Zeichenketten immer auf das gleiche Objekt beziehen.

:a.object_id == :a.object_id
 => true
'a'.object_id == 'a'.object_id
 => false

Sie werden auch häufig verwendet, um auf Methodennamen zu verweisen, und kann tatsächlich umgewandelt werden in Proc s , die an Methoden übergeben werden können. Dies ermöglicht es uns, Dinge zu schreiben wie array.map &:to_s .

Auschecken dieser Artikel für weitere Interpretationen des Symbols.

1voto

jsinger Punkte 797

Für die Definition von inject werden Sie im Grunde genommen verkettete Blöcke einrichten. Konkret bedeutet das, dass die Variable n in {|value| n = yield(n, value)} ist im Wesentlichen ein Akkumulator für den Block, der an inject . So kann zum Beispiel für die Definition von product , inject(1) {|value| n * value} Nehmen wir an, Sie haben ein Array my_array = [1, 2, 3, 4] . Wenn Sie anrufen my_array.product beginnen Sie mit dem Aufruf inject mit n = 1. each führt zu dem Block, der in inject , der wiederum zu dem Block führt, der an inject selbst mit n (1) und dem ersten Wert in der Matrix (in diesem Fall ebenfalls 1). Dieser Block, {|n, value| n * value} gibt 1 == 1 * 1 zurück, das heißt, es wird inject die Variable n. Als nächstes wird 2 aus jeder Variable abgeleitet, und der Block, der in inject Block ergibt sich als yield(1, 2) die 2 zurückgibt und sie n zuordnet. Als nächstes wird 3 aus each liefert der Block die Werte (2, 3) und gibt 6 zurück, die als nächster Wert in n gespeichert wird, usw. Im Wesentlichen ist die Verfolgung des Gesamtwerts unabhängig von der Berechnung, die in den spezialisierten Routinen durchgeführt wird ( sum y product ) ermöglicht eine Verallgemeinerung. Ohne dies müsste man z.B. deklarieren

def sum
  n = 0
  each {|val| n += val}
end

def product
  n = 1
  each {|val| n *= val}
end

die sich auf ärgerliche Weise ständig wiederholt.

Zu Ihrer zweiten Frage, attr_reader und seine Familie sind selbst Methoden, die die entsprechenden Zugriffsroutinen mit define_method intern, in einem Prozess, der Metaprogrammierung genannt wird; sie sind keine Sprachanweisungen, sondern einfach nur Methoden. Diese Funktionen erwarten die Übergabe eines Symbols (oder vielleicht einer Zeichenkette), das den Namen der Accessors angibt, die Sie erstellen. Theoretisch könnten Sie Instanzvariablen verwenden wie @member hier, obwohl es der Wert wäre, auf den @member Punkte, die in die folgende Tabelle übernommen und verwendet werden define_method . Ein Beispiel dafür, wie diese umgesetzt werden, diese Seite zeigt einige Beispiele für attr_*-Methoden.

1voto

Frederick Cheung Punkte 81702
def inject(accumulator)
  each { |value| accumulator = yield(accumulator, value) }
  accumulator
end

Dies ergibt lediglich den aktuellen Wert von accumulator und das Array-Element in den Inject-Block und speichert das Ergebnis dann wieder im Akkumulator.

class Example
  attr_reader @member
end

attr_reader ist einfach eine Methode, deren Argument der Name des Accessors ist, den Sie einrichten wollen. Auf eine konstruierte Art und Weise könnten Sie also Folgendes tun

class Example
  @ivar_name = 'foo'
  attr_reader @ivar_name
end

um eine Getter-Methode namens foo

1voto

Edd Steel Punkte 709

Ihre Verwirrung über das erste Beispiel könnte darauf zurückzuführen sein, dass Sie Folgendes gelesen haben |value| n als ein einziger Ausdruck, aber das ist er nicht.

Diese umformatierte Version könnte für Sie verständlicher sein:

 def inject(n)
   each do |value|
     n = yield(n, value)
   end
   return n
 end

value ist ein Element in dem Array und wird mit n an den Block übergeben, der an inject , dessen Ergebnis auf n . Wenn das nicht klar ist, informieren Sie sich über die each Methode, die einen Block annimmt und jedes Element im Array an diesen zurückgibt. Dann sollte es klarer sein, wie die Akkumulation funktioniert.

attr_reader ist weniger seltsam, wenn man bedenkt, dass es sich um eine Methode zur Erzeugung von Accessor-Methoden handelt. Es ist kein Accessor an sich. Sie muss sich nicht mit dem @member den Wert der Variablen, sondern nur ihren Namen. :member ist nur die internierte Version der Zeichenkette 'member', die der Name der Variablen ist.

Sie können sich Symbole als leichtere Zeichenketten vorstellen, mit dem zusätzlichen Vorteil, dass jedes gleiche Etikett dasselbe Objekt ist. :foo.object_id == :foo.object_id in der Erwägung, dass 'foo'.object_id != 'foo'.object_id denn jedes "foo" ist ein neues Objekt. Sie können das selbst ausprobieren in irb . Betrachten Sie sie als Etiketten oder primitive Zeichenketten. Sie sind überraschend nützlich und kommen häufig vor, z.B. bei der Metaprogrammierung oder als Schlüssel in Hashes. Wie bereits an anderer Stelle erwähnt, ist der Aufruf von object.send :foo ist dasselbe wie der Aufruf von object.foo

Es lohnt sich wahrscheinlich, einige frühe Kapitel aus dem Buch zu lesen Spitzhacke"-Buch um mehr Ruby zu lernen, wird es Ihnen helfen, die zusätzlichen Dinge, die Rails hinzufügt, zu verstehen und zu schätzen.

0voto

Zunächst müssen Sie verstehen, wo Sie Symbole verwenden sollten und wo nicht. Ein Symbol wird vor allem verwendet, um etwas darzustellen. Zum Beispiel: :Name, :Alter usw. Hier werden wir keine Operationen damit durchführen. Strings werden nur für die Datenverarbeitung verwendet. Beispiel: 'a = name'. Hier werde ich diese Variable 'a' weiter für andere String-Operationen in Ruby verwenden. Außerdem sind Symbole speichereffizienter als Strings und sie sind unveränderlich. Deshalb bevorzugen Ruby-Entwickler Symbole gegenüber Strings.

Sie können sogar die Methode inject verwenden, um die Summe als (1..5).to_a.inject(:+) zu berechnen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X