Frage zu Aufgabe 4a, Klausur 18.02.2014 (Scala - Teilersummen)

Disclaimer: Dieser Thread wurde aus dem alten Forum importiert. Daher werden eventuell nicht alle Formatierungen richtig angezeigt. Der ursprüngliche Thread beginnt im zweiten Post dieses Threads.

Frage zu Aufgabe 4a, Klausur 18.02.2014 (Scala - Teilersummen)
Hallo,

ich verstehe die Aufgabe 4a nicht:

Meine Verständnisprobleme:

  • Die Funktion sum liefert ja schon das Ergebnis von fs(n). Die Funktion fs bekommt ja ein Integer n übergeben und dann würde es ja genügen damit sum(facs(n)) aufzurufen und man hätte das gewünschte Ergebnis. Aber man soll ja stattdessen was kompliziertes mit construct(facs)(sum) machen.
  • Wenn wir nur das def fs ansehen: Woher kommen facs und sum in construct(facs)(sum)?
    ** Erstes Problem: Werden hier die Funktionen facs und sum als Parameter der Funktion construct übergeben?
    ** Zweites Problem: Falls ja, was bekommen dann diese Funktionen in der Funktion construct jeweils übergeben? Das ist ja auch nirgends definiert.
  • Wie darf ich mir also def fs: Int => Int = construct(facs)(sum) vorstellen? Wenn ich die Zeile ein Eclipse eingebe, untrerringelt er mir das mit „type mismatch; found: Int => List[Int] required: Int“. Also scheine nicht nur nich ein Verständnisproblem damit zu haben, sondern auch Eclipse :rolleyes:

Ich versteh’s nicht: Ich stecke in fs ja nur ein Int rein und construct(facs)(sum) entspricht dann ja diesem reingesteckten Int, oder? Korrekt, aber nicht verlangt, wäre ja meiner Meinung nach:

def fs: Int => Int = n => sum(facs(n))

Damit ihr das auch ausprobieren könnt, hier mal die (von mir ausformulierten) Funktionen:

object Teilersummen {
  
  // facs(n) liefert die Teiler von n (ohne n)
  def facs: Int => List[Int] = n => for(x <- List.range(1,n) if n%x == 0) yield x
  
  // sum(xs) summiert alle Zahlen in xs
  def sum: List[Int] => Int = {
    case Nil => 0
    case x::xs => x+sum(xs)
  }
  
  // Teilersumme fs(n) der natürlichen Zahl n sei die Summe ihrer ganzzahligen Teiler ohne n
  def fs: Int => Int = construct(facs)(sum)
  
  // AUFGABE: Geben Sie die hierfür notwendige Funktion construct(f)(s) inklusive Signatur an:
  def construct: Int => List[Int] => Int = n => xs => ??? //???
      
}

Die sind irgendwo definiert. Wichtig ist ohnehin nur deren Verhalten, nicht die konkrete Implementierung.

Nein, du steckst in [m]fs[/m] gar nichts rein.[m] fs[/m] soll eine Funktion zurückgeben, die ein Int nimmt. Diese Funktion wird von [m]construct[/m] aus den übergebenen Argumenten konstruiert. Und diese zurückgegebene Funktion soll dann das tun, was du im Prinzip schon geschrieben hast:

Nochmal kurz und knapp: [m]fs[/m] gibt eine Funktion zurück, ebenso [m]construct[/m]. Diese Funktion hat einen Parameter und soll in [m]construct[/m] aus den [m]construct[/m] übergebenen Argumenten zusammengebaut werden.

Ach ja: Bitte bis zum Ende des Übungsbetriebs noch keine Lösungen posten. Möglicherweise kommt ja noch eine alte Klausuraufgabe als Bonusaufgabe dran. :wink:

1 Like

Vielen Dank für die Antwort! :slight_smile: Aber ich steh noch immer auf dem Schlauch. :-/ Ohne eine Lösung zu nennen: Ich schildere mal meinen Gedankengang und hoffe, dass du dazu was sagen kannst.

Eine Funktion [m]test[/m] welche x abbildet auf x*2 (“Ich stecke ein Int rein und bekomme ein Int zurück”) wäre:

def test: Int => Int = x => x*2

Aufgerufen würde diese Funktion ja beispielsweise mit [m]test(2)[/m].

Dieses Muster habe ich auch auf [m]def fs[/m] in der Angabe angewandt. Siehst du, warum ich meinte, dass [m]construct(facs)(sum)[/m] ein [m]Int[/m] sein muss? Außerdem fehlt mir hier noch der weitere Folgepfeil, der angibt, auf was dieses [m]Int[/m] dann abgebildet werden soll:

def fs: Int => Int = construct(facs)(sum) => ?

Als nächstes war ich verwirrt, was es mit den Parametern [m]facs[/m] und [m]sum[/m] auf sich hat. Denn exakt diese Bezeichnungen wurden zuvor ja als Funktionen-Namen verwendet. Also sind die doch hier schon in der Angabe definiert worden und nicht “irgendwo”? Ich darf ja [m]facs[/m] nicht zweimal definieren. Also gehe ich davon aus, dass in [m]construct[/m] die beiden angegeben Funktionen [m]facs[/m] und [m]sum[/m] als Paremeter übergeben werden. Und an der Stelle ist alles aus bei mir.

Also Klartext:

  • [m]facs[/m] und [m]sum[/m] in [m]construct(facs)(sum)[/m] sind nicht die zuvor definierten Funktionen?
    ** Falls ja: Soll ich dann davon ausgehen, dass irgendwo eine [m]List[Int] facs[/m] und ein [m]Int sum[/m] deklariert sind?
    ** Fals es doch die zuvor definierten Funktionen sind: Dann verstehe ich den Aufruf [m]construct(facs)(sum)[/m] nicht. Dann müsste es ja vielmehr sowas wie [m]construct(facs(n))(sum(facs(n)))[/m] sein, aber das macht absolut keinen Sinn.
  • [m]construct(facs)(sum)[/m] ist ja ein Aufruf mit zwei Parametern (Curry-Prinzip). Also muss die Funktion ja in der Art lauten: [m]def construct: Irgendwas => Irgendwas => Int = x => y => Integer_Wert_der_zurückgegeben_wird[/m] (nach dem Curry-Prinzip und da die Funktion [m]fs[/m] ja ein [m]Int[/m] zurückgeben soll, also auch [m]construct[/m] ein [m]Int[/m] zurückgeben muss).

Ich find das gerade ungeheuer deprimierend, da ich in der Klausur ja nur paar Minuten Zeit hierfür gehabt hätte und ich seit Stunden nicht verstehe was das alles soll. Ich hoffe mich kann einer erleuchten (ohne die Lösung zu nennen) :smiley:


Das ist genau der Punkt! Es ist kein [m]=>[/m] vorhanden. Um das zu erklären, hole ich mal etwas weiter aus:
In der Übung 11 kamen ja anonyme Funktionen dran. Eine anonyme Funktion können wir uns zum Beispiel so deklarieren:

x => 2 * x

Wenn wir die gleiche Funktionalität in einer „normalen“ Funktion beschreiben würden, sähe das so aus:

def foo: Int => Int = x => 2 * x

Jetzt die Preisfrage: Wo kommt die oben genannte anonyme Funktion in der „normalen“ Funktionsdefinition vor?

Wie du siehst, kann man die untere Funktionsdefinition unterschiedlich interpretieren. Einmal als Funktion, die ein Int nimmt und ein Int zurückgibt. Dann aber auch als Alias für die anonyme Funktion oben. Das wiederum kann man interpretieren als Funktion, die einfach eine Funktion zurückgibt. Und voilà, sind wir da, wo wir hinwollen: Wir können nämlich [m]x => 2 * x[/m] ersetzen durch jede beliebige Funktion (vorausgesetzt, die Typen stimmen), also auch eine benannte Funktion. Das ist im Fall von [m]fs[/m] eben die Funktion, die von [m]construct(facs)(sum)[/m] zurückgegeben wird.

Doch, [m]facs[/m] und [m]sum[/m] sind die vorher beschriebenen Funktionen. Mit „irgendwo“ meinte ich, dass es für die Aufgabe egal ist, wo die Funktionen definiert wurden. Sie können in der gleichen Datei, aber auch in einem anderen Modul definiert (und dann importiert) worden sein. Wichtig ist nur: Du kannst die Funktionen [m]facs[/m] und [m]sum[/m] benutzen und sie tun das, was sie nach Aufgabenstellung tun sollen.

Du denkst noch zu sehr in Java-Bahnen. In Scala kann man auch ganze Funktionen an eine Funktion übergeben (ähnlich Funktionspointer in C). Ein Beispiel:

def incAndApply: (Int => Int) => Int => Int = f => n => f(n + 1)

Diese Funktion nimmt als Argumente eine Funktion (f) und eine Zahl (n) und wertet die Funktion f mit dem Argument n + 1 aus.

Die Funktion [m]construct[/m] soll selbst eine Funktion zurückgeben, also muss am Ende [m](Int => Int)[/m] stehen.

Hast du dir mal die Bonusaufgabe zur Collatz-Funktion von Blatt 11 angesehen? Das ist das Gleiche in etwas einfacher. :wink:

1 Like

Vielen Dank, jetzt hab’s ich verstanden :slight_smile:

Noch eine letzte Frage: Wenn ich unten stehende Funktionen [m]foo[/m] und [m]bar[/m] definiert habe, diese einer weiteren Funktion [m]something[/m] als Parameter übergebe, aber in der Definition dieser Funktion ([m]something[/m]) die gleichen Funktionennamen verwende: Nimmt sich der Compiler dann die oben definierten Funktionen (unabhängig von den übergebenen Parametern) oder nimmt er die als Parameter übergebenen Funktionen ungeachtet dessen, dass sie denselben Namen haben wie die bereits definierten?

def foo: Int => Int = n => n+1
def bar: Int => Int = n => n-1

def doSomething: Int => Int = n => something(foo)(bar)(n)

def something: (Int => Int) => (Int => Int) => Int => Int = foo => bar => n => if(n>0) bar(n) else foo(n)

Hintergrund: Ich könnte [m]something[/m] ja auch so definieren:

def something: (Int => Int) => (Int => Int) => Int => Int = f => b => n => if(n>0) b(n) else f(n)

Hier bin ich mir sicher, dass er dann auch wirklich die Funktionen nimmt, die ich als Paremeter übergeben habe. Wie ist es also, wenn ich’s wie in der ersten Variante oben schreibe?

Danke! :smiley:


Der Compiler wählt die als Parameter übergebenen Funktionen. Das kannst du zum Beispiel testen, indem du einfach [m]something(bar)(foo)(n)[/m] aufrufst. :wink: