[QUIZ#8] kuddeldaddeldu (Ruby)

kuddeldaddeldu

Erfahrenes Mitglied
Hi,

Ruby gibt's nur unvollständig, da ich mich gleich ins Wochenende verabschiede.

Edit: Verwendet habe ich den Ruby User's Guide und Programmierung in Ruby, beides gefunden über http://www.ruby-lang.org. All zu überzeugend fand ich beide vom Aufbau her nicht, aber für einen ersten Einstieg reicht es.

Begrüßer
Ruby:
#! /usr/bin/ruby

print "Bitte geben Sie Ihren Namen ein: "
puts "Hallo " << gets.strip << "!"

Quadratzahlen
Ruby:
#! /usr/bin/ruby

print "Obergrenze: "
n = gets.to_i
for i in 1..n
   square = i*i
   if square <= n
      puts square
   else
      break
   end
end

Fakultät & Fibonacci
Ruby:
#! /usr/bin/ruby
class Formeln

   def initialize(n)
      @n = n
   end
   
   def fak
      fak = 1
      for i in 1..@n
         fak *= i
      end
      return fak
   end
   
   def fib
      x,y,fib = 1,1,0
      for i in 2..@n-1
         fib = x + y
         x,y = y,fib
      end
      return fib
   end
   
end

print "Zahl: "
n = gets.to_i
formeln = Formeln.new(n)
puts "#{n}! = #{formeln.fak}"
puts "f(#{n}) = #{formeln.fib}"

Links und evtl. verlorengegangene Einrückungen reiche ich dann noch nach...

LG
 

Anhänge

  • quiz8_ruby.zip
    999 Bytes · Aufrufe: 14
Zuletzt bearbeitet:
Hallo kuddeldaddeldu,

ich bin dann mal so frei und gebe als bekennender Rubyist ein paar Kommentare zu deinen Lösungen ab.

Begrüßer
Ruby:
#! /usr/bin/ruby

print "Bitte geben Sie Ihren Namen ein: "
puts "Hallo " << gets.strip << "!"
Einwandfrei. Statt gets.strip könnte man auch gets.chomp verwenden, wenn man nur den Zeilenumbruch am Ende loswerden will. Die Stringkonkatenation mit << könnte man auch durch die Stringinterpolation ersetzen, in etwa so:
Ruby:
puts "Hallo #{gets.strip}!"
Allerdings wäre hier zu überlegen, ob man nicht zuerst ein name = gets.strip macht und dann name ausgibt, da der Aufruf von gets sonst schon ziemlich versteckt ist.

Quadratzahlen
Ruby:
#! /usr/bin/ruby

print "Obergrenze: "
n = gets.to_i
for i in 1..n
   square = i*i
   if square <= n
      puts square
   else
      break
   end
end
for findet man in typischem Ruby-Code eigentlich äußerst selten, stattdessen verwendet man spezialisierte Schleifen. Hier würde sich z.B. upto anbieten:
Ruby:
1.upto(n) do |i|
  # Schleifenkörper wie oben
end
Kleine Anmerkung zum Stil: in Ruby rückt man mit zwei Leerzeichen ein.

Fakultät & Fibonacci
Ruby:
[...]
   def fak
      fak = 1
      for i in 1..@n
	 fak *= i
      end
      return fak
   end
Auch hier würde man wieder eher eine upto-Schleife verwenden oder das ganze gleich zu einem Einzeiler machen:
Ruby:
def fak
  (1..@n).inject(1) {|memo, i| memo * i}
end
Das ist (quasi) äquivalent zu:
Ruby:
def fak
  memo = 1
  (1..@n).each do |i|
    memo = memo * i
  end
  memo
end
Dabei sollte man vielleicht noch erwähnen, dass der Rückgabewert einer Funktion in Ruby einfach der letzte ausgewertete Ausdruck ist, man braucht also nicht unbedingt ein return.

Ruby:
   def fib
      x,y,fib = 1,1,0
      for i in 2..@n-1
	 fib = x + y
	 x,y = y,fib
      end
      return fib
   end
Schöne Anwendung der mehrfachen Zuweisung :) Für die Schleife gilt wieder das Übliche. Auch hier könnte man wieder einen Einzeiler draus machen:
Ruby:
def fib
  (1..@n).inject([0, 1]) {|memo, i| [memo[1], memo[0] + memo[1]]}[0]
end
Hab ich eigentlich schon erwähnt, dass man fast jedes Problem in Ruby mit inject lösen kann? ;)

So, ich hoffe das war jetzt nicht zu viel Kritik auf einmal, aber ich hab einfach mal geschrieben was mir als erfahrender Ruby-Benutzer so aufgefallen ist. Im Prinzip waren deine Programme ja auch alle korrekt, nur eben nicht immer ganz "The Ruby Way".

Grüße, Matthias
 
Hi Matthias,

auch Dir ein Danke fürs Feedback. :)

Die Stringkonkatenation mit << könnte man auch durch die Stringinterpolation ersetzen, in etwa so:
Ruby:
puts "Hallo #{gets.strip}!"
Allerdings wäre hier zu überlegen, ob man nicht zuerst ein name = gets.strip macht und dann name ausgibt, da der Aufruf von gets sonst schon ziemlich versteckt ist.

Ja, die Möglichkeit habe ich erst später entdeckt. Gibt es da eine Art Coding Standard, z.B. dass man nur Variablen im String verwendet und keine Funktionen? Wobei eine Variable im String ja nicht weniger versteckt ist...

for findet man in typischem Ruby-Code eigentlich äußerst selten, stattdessen verwendet man spezialisierte Schleifen. Hier würde sich z.B. upto anbieten:
Ruby:
1.upto(n) do |i|
  # Schleifenkörper wie oben
end

Die ganzen Iteratoren müsste ich mir tatsächlich mal anschauen, da gibt's ja so einige.

Schöne Anwendung der mehrfachen Zuweisung :)

Dieses Feature gefällt mir auch ausgesprochen gut. :)

Für die Schleife gilt wieder das Übliche. Auch hier könnte man wieder einen Einzeiler draus machen:
Ruby:
def fib
  (1..@n).inject([0, 1]) {|memo, i| [memo[1], memo[0] + memo[1]]}[0]
end
Hab ich eigentlich schon erwähnt, dass man fast jedes Problem in Ruby mit inject lösen kann? ;)

Diese Methode habe ich erst gar nicht gefunden, sieht aber tatsächlich mächtig aus. Ich könnte mir allerdings vorstellen, dass das zu ziemlich kryptischem Code führen kann, wenn man's übertreibt. Auf die fib-Methode musste ich jetzt schon einen Moment lang "starren". :-(
Das ist aber vielleicht auch Gewöhnungssache?

Alex hatte übrigens in meinem C#-Thread angemerkt, dass bei dieser Aufgabenstellung eine Klasse mit statischen Methoden, die einen Parameter erwarten, sinnvoller wäre, womit er meiner Meinung nach recht hat. Das würde in Ruby bedeuten, dass man die Funktionen in ein Modul packt? Etwa so:

Ruby:
#! /usr/bin/ruby
module Formeln

   def fak(n)
      (1..n).inject(1) {|memo, i| memo * i}
   end
   
   def fib(n)
      (1..n).inject([0, 1]) {|memo, i| [memo[1], memo[0] + memo[1]]}[0]
   end
   
   module_function :fak
   module_function :fib
   
end

include Formeln

print "Zahl: "
n = gets.to_i
puts "#{n}! = #{fak(n)}"
puts "f(#{n}) = #{fib(n)}"

So, ich hoffe das war jetzt nicht zu viel Kritik auf einmal,

Nö, dafür sind wir ja hier. :)

Im Prinzip waren deine Programme ja auch alle korrekt, nur eben nicht immer ganz "The Ruby Way".

Das hatte ich auch gar nicht erwartet. ;)
Hast Du vielleicht abgesehen von den API-Docs noch die ein oder andere gute Online-Quelle zur Rubyprogrammierung? Meine beiden verwendeten haben mich, wie gesagt, nicht sonderlich überzeugt.

LG
 
Ja, die Möglichkeit habe ich erst später entdeckt. Gibt es da eine Art Coding Standard, z.B. dass man nur Variablen im String verwendet und keine Funktionen?
Nicht dass ich wüsste. Gesunder Menschenverstand sollte da aber ausreichen. Gegen die Verwendung einer reinen Funktion (ohne Nebeneffekte) in einer Stringinterpolation hätte ich auch nichts einzuwenden. Ein
Ruby:
name = gets
puts "Hallo #{name.strip}!"
fände ich durchaus ok.

Wobei eine Variable im String ja nicht weniger versteckt ist...
gets ist aber eine Funktion mit Nebeneffekt, das find ich schone eine Stufe fieser als nur eine Variable einzubinden.

Die ganzen Iteratoren müsste ich mir tatsächlich mal anschauen, da gibt's ja so einige.
Vor allem kann man sich auch flugs eine eigene "Schleifenmethode" bauen:
Ruby:
class Array
  def reverse_each
    (self.size - 1).downto(0) do |i|
      yield self[i]
    end
  end
end

# Gibt das Array rückwärts aus
[1,2,3,4].reverse_each do |n|
  puts n
end
Man sieht hier auch sehr schön, dass man in Ruby eine Klasse jederzeit erweitern kann, sogar die aus dem Core.

Diese Methode habe ich erst gar nicht gefunden, sieht aber tatsächlich mächtig aus. Ich könnte mir allerdings vorstellen, dass das zu ziemlich kryptischem Code führen kann, wenn man's übertreibt. Auf die fib-Methode musste ich jetzt schon einen Moment lang "starren". :-(
Die fib-Methode mit inject ist auch eher mit einem Augenzwinkern zu verstehen. Auf rubyforen.de ist es fast schon ein Running Gag, einen Beitrag mit "Aber das geht doch auch mit inject!" zu schreiben, falls im jeweiligen Thema noch nicht geschehen.

Das ist aber vielleicht auch Gewöhnungssache?
Wenn man ein bisschen Erfahrung in funktionaler Programmierung hat, schreckt einem ein solches inject-Konstrukt nicht mehr so leicht ab. Man kann sich also schon daran gewöhnen.

Alex hatte übrigens in meinem C#-Thread angemerkt, dass bei dieser Aufgabenstellung eine Klasse mit statischen Methoden, die einen Parameter erwarten, sinnvoller wäre, womit er meiner Meinung nach recht hat. Das würde in Ruby bedeuten, dass man die Funktionen in ein Modul packt? Etwa so: [...]
Ja, genau. Wobei du das module_function nicht unbedingt brauchst. Das hat nur eine Bedeutung, wenn du das Modul in eine Klasse einbindest. Einen wirklichen Vorteil eines Moduls sehe ich in diesem Fall aber nicht. Wenn du die Methoden danach sowieso in den globalen Namensraum inkludierst, kannst du sie auch gleich ganz ohne Modul hinschreiben.

Hast Du vielleicht abgesehen von den API-Docs noch die ein oder andere gute Online-Quelle zur Rubyprogrammierung? Meine beiden verwendeten haben mich, wie gesagt, nicht sonderlich überzeugt.
Die "Pickaxe" (das von dir verlinkte "Programmieren in Ruby") ist eigentlich das Standardbuch zu Ruby. Allerdings bezieht sich die im Internet veröffentlichte Fassung auf das inzwischen hoffnungslos veraltete Ruby 1.6.x und ist deshalb nicht uneingeschränkt zu empfehlen.
Eine "etwas" schräge Einführung in Ruby ist Why's (Poignant) Guide to Ruby, welche aber eher Programmieranfänger anspricht. Trotzdem unterhaltsam und lesenswert.
Die Ruby-Grundlagen aus dem Buch "Rapid Web Development mit Ruby on Rails" (PDF, 210kB) sind auch einen Blick Wert.
Ich persönlich hatte die ersten Schritte in Ruby mit der Pickaxe und dem Poignant Guide gemacht und bin damit eigentlich ganz gut zurechtgekommen.

Grüße, Matthias
 
Zurück