ich habe mich jetzt ein wenig in Scala reingelesen und kompiliere immer über die Konsole.
Erste frage: Kann ich da mehrere oder ein Ordner mit .scala Dateien kompilieren?
Weiter habe ich folgendes Anliegen.
Ich möchte gerne mein Trait(Merkmal, Characteristik) generisch machen und zwar für einen Taschenrechner.
Java:
/**
* Trait: ArithmenticOperation.
* A trait encapsulates method and field definitions, which can
* be reused by mixing them into a class.
*/
trait ArithmeticOperation{/**
* add two integer values (concrete implementation)
*/
def add(x:Int, y:Int)= x.+(y)// same like x + y/**
* sub two integer values (concrete implementation)
*/
def sub(x:Int, y:Int)= x.-(y)// same like x - y/**
* multiply two integer values (concrete implementation)
*/
def mul(x:Int, y:Int)= x.*(y)// same like x * y/**
* divide two integer values (concrete implementation)
*/
def div(x:Int, y:Int){require(y.!=(0))// precondition that y must be non-zero
x./(y)// same like x / y}}
Ich kenne das aus Java mit [T] am Klassennamen.
Der Trait soll mit Int,Float und Double , Long funktionieren.
Vielen Dank für Tipps und Tricks, grüße spin:rtfm:
1.) Sollte eigentlich ähnlich wie in Java funktionieren (außer dass man mehrere Klassen in ein Scala-File packen kann). Ich selbst nehme NetBeans mit Scala-Plugin, und bin ganz zufrieden damit. Eclipse mit Scala gibt es fix und fertig: Scala IDE for Eclipse.
0) Ich bevorzuge es, einfach alles direkt mit maven von der Konsole aus zu kompilieren, und einen einfachten texteditor mit syntax-highlighting zu benutzen, damit kannst du dir Elefantenherde kompilieren
1) wozu.-(die komische syntax).? Genau dafür hat man doch die infix-syntax für methoden mit einem Argument
2) Eine solche Klasse ist eigentlich unnötig: zum einen gibt's schon Numeric[T], zum anderen Function[X, Y], je nach dem was besser passt.
3) Parser Combinators sind lustig: damit könnte man nämlich eine ordentliche eingabe basteln, ohne diesen ganzen gui-murks
Ich bin dabei ein Scala Seminar für mein Studium vorzubereiten und brauche daher ein Sample Projekt.
Deswegen versuche ich es so strange wie mgl aufzubauen, damit man die unterscheide sofort zu java erkennt.
Ich habe wirklich ein problem mit der funktionalen Schreibweise:
Java:
/**
* list of digits
*/privatevar digitList:List[String]=List("0","1","2","3","4","5","6","7","8","9")// same like List.apply()/**
* list of buttons
*/privatevar digitButtonList:List[Button]=List()/**
* swing main method ( top level GUI component )
*/
override def top =newMainFrame{
title ="Calculator"// def title_=(s:String)
digitList.zipWithIndex.foreach{
digitButtonList =>case(digit,index)=>newButton{
text = digit
}}
Meine foreach geht nicht
Ich möchte gerne button von 1-9 haben. In Java ist das ziemlich einfach, aber in Scala tut man sich ziemlich schwer damit.
Bitte bitte um Hilfe, werde den Thread sicherlich noch öfters brauchen.;(
Alternativ:
Java:
digitList.zipWithIndex.foreach{case(digit,index)=>
val button =newButton{
text = digit
}
digitButtonList.apply(index)= button
}
Alles klar, aber dann weise bitte vor dem Vortrag ausdrücklich darauf hin, dass dieser "so stsnge wie mgl"-Code auf deinem Mist gewachsen ist, und dass kein normaler scala-Programmierer das so schreiben würde.
Wenn du ein "Scala-Versuchstier" suchst, stelle ich mich gerne zur Verfügung, und schreibe dir einen gut kommentierten Taschenrechner, aber in klaren Scala, nicht im möglichst obskuren Scala.
Code:
private var digitList: List[String] = List("0","1","2","3","4","5","6","7","8","9") // same like List.apply()
das ist Unfug, welcher normaler Mensch schreibt ziffern von 0 bis 9 per hand aus?
Warum ist es "var" und kein val?
Wenn du wirklich haufen Strings haben willst, dann schreib
Code:
val digitStrings = for ( i <- 0 to 9 ) yield i.toString
wobei ich in deinem code nicht nachvollziehen kann, wozu die behämmerten Strings gut sein sollen...
Code:
private var digitButtonList:List[Button] = List()
Was das schon wieder? Wieso "var"? Wieso leer?
Wenn du einfach nur eine Liste mit zehn Buttons erstellen willst, dann geht das so:
Code:
val buttons = for (i <- 0 to 9) yield new Button(i)
Die methoden foreach, map, flatMap und filter sollte man ohne Grund nicht explizit im Scala-Code verwenden, für all das sind for-Konstrukte wesentlich besser geeignet.
Dass das hier ebenfalls gröbster Unfug ist, ist dir hoffentlich auch bewusst:
Ich versuche mich vorsichtig an Scala heran und bin euch sehr dankbar für konstruktive Kritik.
Mein Vortrag muss ich spätestens mitte Oktober halten und entsprechend eine Power-Point Präsi und 20 Seiten (English) Dokumentation fertigstellen.
Um die jeweiligen Konzepte darzustellen, wollte ich einen Taschenrechner programmieren, der zum einen Funktionallen und Objektorientierten Stil verbindet. Weiterhin wollte ich alle Built-In Control Structures verwenden, Traits, Inheritance, Currying, Lists, Generics, Literals, Closures, Anonymous, Infix, Type Bounds
Der Taschenrechner kann also so komisch aussehen wie er will, hauptsache es sind die Konzepte zu erkennen.
Ich erwarte hier von keinen, dass hier jemand für mich lange Zeit investiert. Ich freue mich immer auf Kritik und gott sei dank gibt es immer wieder den einen oder andern, der einfach besser Bescheid weiss
Der Taschenrechener soll eine kleine Swing Anwendung. Da bietet Scala anscheind auch verschiedene Patterns , wie das Actor Pattern.
Ich lese gerade das Buch : Programming in Scala
Doch habe ich immense Schwierigkeiten gleich los zu programmieren. Die Syntax ist ein Problem und die Konzepte sind für mich neu.
Ich progge dann gerne den Taschenrechner mal weiter, werde aber sicher wieder an meine Grenzen kommen.
PS: Ich bin gerne Bereit mein Doku und Präsi dann hier für neue zu Verfügung zu stellen, soweit sie von euch auch gesegnet ist
Wieso hält man einen Vortrag über ein Thema von dem man keine Ahnung hat? Wenn derjenige, der das Projekt bewertet, sich mit Scala auskennt, dann kannst du ganz schön auf die Nase fallen.
Um Konzepte wie Currying und Closures im Detail zu verstehen benötigt es mehr Zeit als den Monat, der dir noch bleibt.
Ich würde vorschlagen, du beschränkst dich ein wenig, und zeigst lieber einige interessante Features etwas detaillierter, anstatt eine Tour de Force durch unbekanntes Gebiet zu reiten.
Was du hier bis dahin geboten hast war aber fürchterlich und radikal anti-idiomatisch, so bringst du garantiert keine Scala-Konzepte rüber, sondern sabotierst im besten Fall 20-25 Leuten den Spaß an Scala^^
Über ein ganzes Buch kannst du garantiert kein Referat halten, da bräuchte man schon eher eine Vorlesungsreihe. Alle Techniken wirst du auch nicht für einen Taschenrechner verbraten können, schränk das ein.
Ungefähr so baue ich es denn jetzt auf. Alle Konzepte sind sicherlich nicht mit einmal lesen und ausprobieren verstanden, aber ich versuch mein bestes.
Den Taschenrechner lasse ich mir denn nicht nehmen - dann versuche ich den simple zu gestalten.
Wobei ich ja nicht mal die for hinbekommen habe ^^.
Gilt also noch viel auszuprobieren
Anregungen sind super herzlich willkommen.
Welche Themen würde euch denn spontan einfallen um zu zeigen wie sich Scala und Java differenzieren.
- Spar dir den Geschichtsunterricht, erwähne in einer Zeile, von welchen Sprachen Scala inspiriert wurde.
- Was sollen diese "Design Goals" sein? Wenn du da was von übersichtlichkeit des Codes erzählst, und später mit "arr.apply(3) = 5" kommst, kannst du dir das ganze eigentlich sparen.
- Was verstehst du unter "Characteristics"?
- Functions kommt an 2. Stelle, nicht an letzte, es ist doch funktionale Sprache
- Sowas wie "Vererbung" und "Polymorphismus" ist nichts Scala-Spezifisches, sondern seit zwei jahrzehnten durchgekaute OOP-basics, darf man ja wohl voraussetzen
- Traits erwähnen, die ganzen spezielleren konstrukten wie modifikationen mit [c]with[/c] bei der instantiierung sollte man weglassen: wird viel seltener verwendet, und kriegt man eh nicht in einer Minute erklärt.
...
Allgemein: mach weniger Blah-Blah, welches allen eh schon bekannt ist, entferne Details, die eh keiner verstehen kann, mach kurze beispiele, bei langen beispielen schaltet das publikum eh ab.
Ungefähr so baue ich es denn jetzt auf. Alle Konzepte sind sicherlich nicht mit einmal lesen und ausprobieren verstanden, aber ich versuch mein bestes.
Warum ist das ganze auf Englisch? Ist das für die Uni oder wofür?
Warum erklärst du die einzelnen Konzepte nicht an kleinen isolierten Beispielen? Das bringt tausend mal mehr, als irgendein unübersichtliches Taschenrechner-Programm, in dem du nach einem Monat selbst nicht mehr durchblickst.
Den Taschenrechner lasse ich mir denn nicht nehmen - dann versuche ich den simple zu gestalten.
Wobei ich ja nicht mal die for hinbekommen habe ^^.
Erzählt mir bitte endlich mal einer, wieso alle so scharf auf diese GUI-Taschenrechner sind? Hast du den Taschenrechner von Google gesehen, siehst du da irgendwelche Knöpfe und Hebel dran? Warum machst du nicht stattdessen irgendetwas sinnvolleres und übersichtlicheres (ohne GUI-Rumgedöns).
Welche Themen würde euch denn spontan einfallen um zu zeigen wie sich Scala und Java differenzieren.
der komplette 1h Vortrag soll auf English sein und die Dokumentation natürlich auch.
Das schreckt mich eher weniger ab
Ok versuche ich doch einfach nochmal zu erzählen, was ich gerne erreichen möchte bzw. was die Ziele des Vortrages sind. Wir sollen eine Einführung in die Sprache geben und einen indirekten Vergleich mit Java machen, weil das die Sprache ist die uns bis zum 4ten Semester an der Uni begleitet hat.
Da Scala auf Java aufgebaut ist und irgendwie nur die nativen Java Methoden mappt, gibt es sicherlich keine riesigen semantischen Unterschiede. Die Art und Weise ist aber eine komplett Andere und vorallem der funktionale Teil ist gewöhnungsbedürftig --- viel von ML und Haskell , Erlang.
Ich möchte schon mit einem geschichtlichen Einstieg starten und vorallem warum Scala entwickelt wurde sowie welche neuen Vorteile sich dadurch ergeben haben.
Mit Design Goals meine ich halt Ziele des Entwurfs von Scala.
Natürlich ist dann ein syntaktischer Einstieg erstmal interessant und anschließend die neuen Konzepte.
--
Wir sollen eine kleine Anwendung dazu bauen, egal ob viel Code oder nicht. Sie muss auch keine GUI Anwendung sein-
Was würdest du denn für einen Anfänger vorschlagen?
Einen Algorithmus Bubblesort, Quicksort, A* , Greedy Best Search, oder so?
Einen Konsolen Taschenrechner?
Eine Bewegte Animation?
Was weiss ich - irgendetwas wo man viele Built-In Controll Structures sieht. --- Ich will ja Scala auch bischen können ---
Da Scala auf Java aufgebaut ist und irgendwie nur die nativen Java Methoden mappt, gibt es sicherlich keine riesigen semantischen Unterschiede. Die Art und Weise ist aber eine komplett Andere und vorallem der funktionale Teil ist gewöhnungsbedürftig --- viel von ML und Haskell , Erlang.
Scala ist keine "Java-Erweiterung", sondern eine eigenständige Sprache mit eigener Syntax, genau wie Clojure oder JRuby. Was Scala mit Java gemeinsam hat, ist im Prinzip nur die Abarbeitung auf der JVM, also das Bytecode-Format der class-Dateien. Deshalb ist es auch möglich, Java aus Scala aufzurufen (hier wurde darauf geachtet, dass das recht reibungslos funktioniert) und umgekehrt (das ist schon wesentlich tückischer).
"Native Methoden" sind Methoden, die bei Aufruf an externe C/C++ Funktionen (z.B. in DLLs) über das Java Native Interface weitergeleitet werden. Du meinst wahrscheinlich etwas anderes, aber "native Methoden" ist hier der falsche Begriff.
N. E. I. N. Nein. Scala ist kein umdekoriertes Java, es verhält sich auch nicht wie C++ zu C oder irgendsowas ähnliches, trotz aller Kompatibilität. Das ist eine ganz falsche Start-Voraussetzung. Beim OOP-Teil gibt es zwar einige syntaktisch-semantische Gemeinsamkeiten, aber darüber und darunter sieht es bis zum Byte-Code ziemlich anders aus, und das sollte man als Java-Programmierer verstehen, wenn man damit anfängt, sich die Sprache anzuguggen.
Ich möchte schon mit einem geschichtlichen Einstieg starten und vorallem warum Scala entwickelt wurde sowie welche neuen Vorteile sich dadurch ergeben haben.
Mit Design Goals meine ich halt Ziele des Entwurfs von Scala.
Wenn du unbedingt was mit "rechnen" haben willst, da habe ich was geiles für dich: ein Plotter der Newton-Fraktale für Polynome der Gestalt
Code:
x^5-4*x^7-13*x^2+x-7
Daran könntest du ganz einfach folgende Sachen demonstrieren:
- implementierung der Complexen Zahlen: jeder weiß worum's geht, perfekt um Grundlagen der OOP, case-classes, pattern matching, object-statt-static-methoden und "Operator-überladung" zu demonstrieren
- perfekt um implizite conversions der doubles zu complex zu demonstrieren
- Für ableitung der polynome braucht man eine Schleife, Auswertung der Polynome könnte man mit hilfe von foldLeft o.ä. demonstrieren
- Newton-verfahren zu implementieren ist ganz leicht, einzeiler mit einer while- oder for-schleife
- Parser-Combinators sollten für polynome ganz simpel sein (viel einfacher als ganzer Taschenrechner), aber genug, um die technik zu demonstrieren
- Auswertung eines solchen Fraktals kann trivial parallelisiert werden: jeder pixel kann einfach einzeln berechnet werden, daran kann man parallele collections demonstrieren
- Ein bisschen GUI, um die rückwärtskompatbilität mit den tollen Java-Bibliotheken zu demonstrieren, indem man das Bild anzeigt.
- Die Bilder sehen geil aus
Du siehst: ich studiere halt mathe, ich hab nur komische mathematische beispiele auf Lager :bae:
ich habe ziemliche Angst, dass ich dass dann nich schaffe.
Klar kann ich loslegen die Complexen Zahlen zu implementieren, aber ich wäre dann über deine Hilfestellung sehr erfreut. Alleine werde ich dass nicht schaffen.
Hier habe ich schonmal die komplexen Zahlen abgebildet.
Java:
/**
* Trait: ArithmenticOperation.
* A trait encapsulates method and field definitions, which can
* be reused by mixing them ComplexNumbero a class.
*/
trait Arithmetic{/**
* add two complex numbers (concrete implementation)
*/
def add(x:ComplexNumber, y:ComplexNumber):ComplexNumber=newComplexNumber(
x.real + y.real ,
x.im + y.im
)/**
* sub two complex numbers values (concrete implementation)
*/
def sub(x:ComplexNumber, y:ComplexNumber):ComplexNumber=newComplexNumber(
x.real - y.real ,
x.im - y.im
)/**
* multiply two complex numbers values (concrete implementation)
*/
def mul(x:ComplexNumber, y:ComplexNumber):ComplexNumber=newComplexNumber(
x.real * y.real - x.im * y.im,
x.real - y.im + x.im * y.real
)/**
* divide two complex numbers values (concrete implementation)
*/
def div(x:ComplexNumber, y:ComplexNumber):ComplexNumber=newComplexNumber((x.real * y.real + x.im * y.im)/(y.real * y.real + y.im * y.im),(-x.real * y.im + y.real * x.im)/(y.real * y.real + y.im * y.im))}
Java:
/**
* Class to design a complex number.
*/classComplexNumber(r:Double,i:Double){/**
* real part of a complex number
*/var real:Double= r
/**
* imaginary part of a complex digit
*/var im:Double= i
/**
* absolute value
*/
def abs =Math.sqrt(r*r + i*i);}
Code:
jeder weiß worum's geht, perfekt um Grundlagen der OOP, case-classes, pattern matching, object-statt-static-methoden und "Operator-überladung" zu demonstrieren
Ich habe jetzt in den Klassen OOP drin ^^. Polymorphismus aber mehr auch nicht an spannenden sachen.
Wo würdest du hier jetzt case classes hinpacken?
Bzw.. an welcher Stelle kann ich Konzepte besser verdeutlichen?
So ich schau mal wie ich das Polynom reinbekomme.:rtfm:
Hey, Spin, nein, vergiss erstmal dieses "Numeric[T]"-pattern, okay? Das ist ein bisschen zu viel rocket-Science erstmal, hier geht es nicht darum, code für irgendwelche speziellen nativen datentypen zu optimieren, sondern um die Basics, Numeric[T] ist dagegen schon eher fortgeschrittenes Thema.
Also, ich kann mir sehr gut vorstellen, dass man all diese Konzepte wie folgt demonstrieren kann:
Code:
package spinsExampleProject
// hier werden gleich Traits und einfache Generics demonstriert
trait Arithmetic[X <: Arithmetic[X]]{
self: X => // definition des self-types, sehr nützlich, um festzulegen, dass "this" später Typ X haben wird
// haufen abstrakter methoden, die von der Art der Zahlen abhängt
def one: X
def *(other: X): X
def inverse: X
def /(other: X) = this * other.inverse // das lässt sich bereits durch andere methoden ausdrücken!
def zero: X
def +(other: X): X
def unary_- : X // ein typisches Beispiel für "Operator Overloading"
def -(other: X) = this + -other // das lässt sich auch durch andere methoden ausdrücken, obwohl's ineffezient ist
// einige nützliche methoden, die _nicht_ von der Art der Zahlen abhängen,
// sondern überall gleich sind (für reelle und komplexe zahlen, endliche körper usw.)
// Das ist der entscheidende Unterschied von Interfaces und traits: bei traits kann man etwas
// implementieren!
// einfaches quadrieren:
def square = this * this // hier kommt ins spiel, dass self vom typ X ist, und an *(x: X) übergeben werden kann
// hier könnte man das square-and-multiply verfahren (bauern-multiplikation) basteln,
// aber du kannst dich ruhig mit einfachen linearen rekursion begnügen, soll ja nur Demo sein
def pow(i: Int): X = {
if (i < 0)
inverse.pow(-i)
else if (i == 0)
one
else
this * this.pow(i - 1)
}
}
Code:
package spinsExampleProject
// Demo eines case-classes
case class Complex(re: Double, im: Double) extends Arithmetic[Complex]{
// hier muss man nur 6 methoden implementieren und kriegt so "komplizierten" Kram wie pow() geschenkt
// [es ist nicht kompliziert, aber für die Idee der "Rich interfaces" sollte's reichen]
def zero = Complex(0, 0)
def +(c: Complex) = Complex(re + c.re, im + c.im)
def unary_- = Complex(-re, -im)
def one = Complex(1, 0)
def *(c: Complex) = Complex(re * c.re - im * c.im, re * c.im + im * c.re)
def inverse = {
val sqLen = re * re + im * im
Complex(re / sqLen, -im / sqLen)
}
}
object Complex{
// statische member werden in Scala in companion-objects gespeichert
val I = Complex(0, 1)
// sehr wichtig: implicite casts
implicit def castDoubleToComplex(d: Double) = Complex(d, 0)
}
Code:
package spinsExampleProject
import Complex._
import math._
object UsageExample1 {
def main(args: Array[String]){
// hier sieht man, dass man nichts sieht:
// Dank impliciten casts benehmen sich Doubles plötzlich wie Complex!
val x = 1.5 + 0.3 * I
val y = 7.2 - 0.2 * I
// man achte darauf, dass die Ausgabe einigermaßen leserlich ist, obwohl toString nicht ünerschrieben wurde
println( x * y )
// und so funktioniert pattern-matching, man "dekonstruiert" eine Komplexe zahl wieder in Einzelteile:
val Complex(a, b) = x * ( y + 23.0 - 7.3 * I)
println("a = " + a + " b = " + b)
// hier ist die anwendung von "pow", welches uns durch Arithmetic umsonst hinterhergeworfen wurde:
val seventhUnityRoot = cos(2 * Pi / 7) + I * sin(2 * Pi / 7)
println(seventhUnityRoot pow 7)
}
}
Wie Du siehts: alleine mit einem so simplen und allgemein-bekannten Beispiel wie den komplexen zahlen kann man schon Folgendes abdecken:
- OOP basics (classes, methods, member-variables, static variables in companion objects)
- traits (and "rich" interfaces)
- generics
- type-bounds
- self-types
- operator overloading
- implicit casts
- case classes
- pattern matching (at least a single basic example)
- general structure of typical programm with a main-method
Und dabei überforderst du dein Publikum (hoffentlich) nicht mit irgendwelchen völlig unbekannten Konzepten, die von der Sprache ablenken (Komplexe zahlen sollen allgemein bekannt sein, alle Knöpfe und hebel des Swing-frameworks dagegen nicht).
Für Polynome könntest du eine generische classe anlegen, etwa Poly[X <: Arithmetic[X] : Manifest] (das bedeutet, dass die Polynome nur irgendwie geartete "Zahlen" speichern können sollen), in der du das polynom einfach in einem array abspeicherst: dabei kannst du hoffentlich klar machen, dass dank Manifest type-erasure gar nicht mehr soo nervtötend ist. Auswerten könntest du das einfach mit dem Horner-Schema, das wäre imho eine hübsche einzeilige anwendung des foldLeft's, auf der man allerdings nicht rumhacken sollte: Horner schema ist zwar sehr billig, ist aber vielleicht nicht jedem ein begriff...
EDIT: achso, noch was wollte ich loswerden: bei Scala präferiert man kurze Notation, in Anlehnung an andere funktionale Sprachen, einfach weil so viele methoden nach einer zeile schon zu ende sind, und für geübtes Auge auch selbsterklärend implementiert. Bei einer Präsentation würde ich den ganzen Dokumentations-Kram weglassen: Dokumenation ist allgemein bekannt, und wurde nicht erst extra für Scala erfunden, und es ist eh klar, dass "+" die complexen zahlen schon nicht logarithmieren und durcheinanderdividieren wird. Und es gibt überhaupt keinen guten Grund, einen zweiparametrigen Komplex-Konstruktor auf vier Zeilen zu ziehen. Warum "r, c, real und im" alle doppelt, und auch noch mutabel gespeichert werden, ist mir rätselhaft: es macht aus performance gründen schon durchaus sinn, sie mutabel zu machen, aber es geht grad nicht um performance, sondern um ein möglcihst minimales Beispiel, welches in 10 Minuten erklärt ist.
Ich schaue mir denn jetzt erstmal deinen Code genauer an und versuche diesen nachzuvollziehen.
Anhand meines Buches : Programming in Scala kann ich dieses vielleicht schnell hinbekommen.
Weiterhin vielen Dank auch für deine Tipps. Du spricht immer wieder von neuen Sachen, die ich immer nur gehört aber noch nie angewendet habe.
Mit dennen muss ich mich jetzt erstmal auseinandersetzen bevor ich mit dem Polynom anfange.
Okay. Ich will was sagen: sorry. Ich schmeiß dir ab jetzt keinen fertigen Code an den Kopf, ich versuch's etwas herausfordernder und didaktisch sinnvoller rüberzubringen, ich will eigentlich dein Beispiel nicht highjacken, aber bei Complexen zahlen rutschen mir gleich große Codeblöcke raus
Je oh je... Das ist alles zwar sehr schön, aber zumindest den letzten Vorschlag muss ich als ganz üblen Hack einstufen: es ist lustig, aber ich würde niemandem ernsthaft empfehlen, das so zu machen. Außerdem sind es genau die Beispiele, wo manifest eben nicht hilft. Für dich sollte manifest erstmal lediglich ein schreibtechnisch angenehmer Trick sein, der es ermöglicht, generische Arrays ohne viel Schmerz anzulegen: das könntest du imho bei der Berechnung der Ableitung der Polynome gut demonstrieren.
Du hast mir bisher sehr viel schweiß und Arbeit erspart...und kann ich dir mit programmier kenntnissen schlecht entgegenkommen. ;(
ich gehe gerade deinen Code durch und verstehe so langsam immer mehr Endlich.
Aber trotzdem habe ich ein paar Fragen :
Java:
def unary_-=Complex(-re,-im)
Warum hat deine Variable hier ein _- ????
See Question in comments , plz.
Java:
package spin;/**
* Class: Complex
* Trait: Arithmetic[T]
*
* A complex number is a number consisting of a real part (re:Double) and an
* imaginary (im:Double) part.
* Class Complex uses a trait called Arithmetic[Complex] to reuse code. This trait
* encapsulates method and field definitions. (Class Mix in)
*
*
* In Addition Class Complex is a case class (sub class).
* Advantages: - no new statement, because it adds a factory method
* - all arguments get a val prefix, so they are maintained as fields
* - adds natural implementations if toString , hashCode, equals
* - adds method copy
*
* Only instances methods:
*
*/caseclassComplex(re:Double, im:Double)extendsArithmetic[Complex]{/**
* A complex number with zero real and zero imaginary part.
*/
override def zero =Complex(0,0)/**
* A complex number with a real but with a zero imaginary part.
*/
override def one =Complex(1,0)/**
* Adds two complex numbers.
*/
override def +(c:Complex)=Complex(re + c.re, im + c.im)/**
* Multiply two complex numbers.
*/
override def *(c:Complex)=Complex(re * c.re - im * c.im, re * c.im + im * c.re)/**
* unary complex number
*/
override def unary_-=Complex(-re,-im)/**
* Was ist das? Das Inverse Element? Was ist mit der Dibision?
*/
override def inverse ={
val sqLen = re * re + im * im
Complex(re / sqLen,-im / sqLen)}}/**
* This object is called companion object, because it has the same name like class Complex
* and it can access other's private features.
*
* Only static methods:
*/
object Complex{/**
* define constant : I
*/final val I=Complex(0,1)// Fehler in der Console : Woher weiss ich jetzt ob ich das object oder die klasse meine./**
* implicit cast from double to complex because this cast is not exiting yet.
* Therefore set imaginary part to zero.
*/
implicit def castDoubleToComplex(d:Double)=Complex(d,0)}
Java:
package spin;/**
* Trait: Arithmetic[T]
*
* A trait is the same like a java interface.
* But in addition traits can be contain definitions (implentation).
*
* Trait Arithmetic[T] uses a Upper Bound - this means that the element type
* of the X must be a subtype of Arithmetic
*/
trait Arithmetic[X<:Arithmetic[X]]{/**
* define a self type - "this" has type X now
*/
self:X=>/**
* abstract methods:
*/
def one:X
def *(other:X):X
def inverse:X
def /(other:X)=this* other.inverse // Wie meinst du dass???
def zero:X
def +(other:X):X
def unary_-:X// ein typisches Beispiel für "Operator Overloading", Warum ist das operator overloading?
def -(other:X)=this+-other
/**
* A trait feature : method definitions.
* "this" is type X
*/
def square =this*this/**
* potenz function - exponent must be a int
*/
def pow(i:Int):X={if(i <0)
inverse.pow(-i)elseif(i ==0)
one
elsethis*this.pow(i -1)}}
Kannst du mir nochmal kurz inverse.pow erklären?
Da fehlt mir gölaube ich das verständnis zu Komplexen Zahlen gerade
Danke und dann schaue ich mal nach dem Polynom.
Und nein ich bin sehr dankbar das du mir viel Arbeit bisher abgenommen hast , denn so habe ich einiges an Verständnis gewonnen. Vielen Dank.
Weiter dank für den Link , der ist auch sehr hilfreich.
Ich nimm mir jetzt ein Polynom.
Dann muss ich es in ein Real Teil und Imaginär teil zerlegen.
Dann Gleichung aufstellen
Dann Ableiten nach Newton , damit ich die gleichung lösen kann.
Bei Bezeichnern dürfen Buchstaben/Zahlen und Sonderzeichen nicht gemixt werden - es muss ein _ dazwischenstehen. Ist auch logisch, sonst wären auch Sachen wie [c]a+b*c[/c] gültige Bezeichner. Das [c]unary_[/c]-Prefix wird natürlich benötigt, um zwischen der unären und binären Verwendung des Operators unterscheiden zu können. Für ein normales binäres Minus kannst du einfach [c]def -(c: Complex) = Complex(re - c.re, im - c.im)[/c] schreiben
Das ist keine "Variable", das ist ein unäres Minus. Minus braucht nämlich nicht 2 Argumente: ein unäres Minus subtrahiert nichts, sondern dreht das Vorzeichen um. Wenn du eine Variable x = 7 hast, dann kannst du "-x" schreiben, und es liefert den Wert -7.
Diese Konstruktion mit "unary_" funktioniert eigentlich nur für Operatoren - und ~, spart aber solchen ärger wie "negate(x)" oder "x.negative" statt dem vertrauten "-x".
* In Addition Class Complex is a case class (sub class).
Warum nicht einfach "complex one"? Bzw warum nicht gleich Kommentar sparen... Ist doch wirklich selbsterklärend, aber dazu habe ich mich ja schon geäußert...
klingt irgendwie nach nichts, sollte wohl "unary minus for complex numbers" sein
/**
* Was ist das? Das Inverse Element? Was ist mit der Dibision?
*/
override def inverse = {
val sqLen = re * re + im * im
Complex(re / sqLen, -im / sqLen)
}
}
Joah, so wird halt das Inverse der komplexen Zahlen berechnet, wenn ich mich nicht gröbst verhauen habe. Division sollte im "echten Leben" zwar aus performance gründen überschrieben werden, dies ist aber nicht dringend notwendig, weil Arithmetic eine default-implementierung mitliefert: a/b ist ja nichts anderes als a*(b^-1) bzw a * (b.inverse) in unserer notation hier.
final val I = Complex(0, 1) // Fehler in der Console : Woher weiss ich jetzt ob ich das object oder die klasse meine.
wie, was, welche Konsole? Das alles kompilliert eigentlich einwandfrei. Complex(0, 1) ruft hier eigentlich das apply() des Companion objektes auf, dieses apply() wird durch case-classes automatisch bereitgestellt: wie gesagt, kein "new" erforderlich.
Java:
*A trait is the same like a java interface.*But in addition traits can be contain definitions (implentation).
eine Formulierung mit "similar to" fänd ich hier angebrachter
def unary_- : X // ein typisches Beispiel für "Operator Overloading", Warum ist das operator overloading?
"this" has type X
oder "this" is of type X
und dann fehlt da noch because of the self-type definition, so method *() can be called on "this"
*/
def square = this * this
Das ist Arithmetic: es funktioniert für _alles_, was du unter "Zahl" verstehst, also auch gewöhnliche reelle Zahlen oder Brüche.
Hier steht im Prinzip nichts anderes, als folgende Banale rekursion:
a^0 = 1
a^n = a*a^(n-1)
so viel Mathe dürfte niemanden überfordern
Diese rekursion verringert den exponenten, und funktioniert natürlich nur für positive exponenten. Damit es auch mit negativen klappt, macht man davor eine zusätzliche Abfrage, und wenn der exponent negativ ist, dann verwendet man
a^-n = (a^-1)^n = a.inverse pow n
Ich nimm mir jetzt ein Polynom.
Dann muss ich es in ein Real Teil und Imaginär teil zerlegen.
Nenene, mach Polynom schön generell für beliebige "Zahlen", (also das, was wir hier "Arithmetic" getauft haben), also sowas in der Art:
Code:
class Poly[A <: Arithmetic[A]]
Ableiten funktioniert immer gleich: du multiplizierst den koeffizienten mit dem exponenten, und verringerst den exponenten um 1. Wenn du die Koeffizienten einfach in einem Array abspeicherst, müsste sowas rauskommen:
a*x^3 + b*x^2 + c*x + d
wird gespeichert als [d, c, b, a] (index im array = exponent)
beim ableiten ergibt sich [c * 1, b * 2, a * 3] (das ist eine einzige, ganz leichte schleife),
was nichts anderes bedeutet, als
3*a*x^2 + 2*b*x + c
Ich merke gerade: man sollte bei Arithmetic noch multiplikation mit ints hinzufügen, und bei complex auch implementieren. Das funktioniert dann aber wirklich für alles, was sich halbwegs wie eine Zahl benimmt: von irgendwelchen Imaginärteilen ist da gar nicht die Rede: man braucht solche gruseligen Details nicht zu kennen, wenn man ein Polynom ist
Dann Gleichung aufstellen
Dann Ableiten nach Newton , damit ich die gleichung lösen kann.
Welche "Gleichung aufstellen"? Nein nein, ich hab's eben deswegen vorgeschlagen, weil es vergleichsweise leicht ist, aber hübsche resultate liefert. Du musst lediglich ein Polynom einlesen, ihn wie oben beschrieben ableiten, und dann für jedes x_0 aus deinem "Bild" (also einem Quadratischen Ausschnitt der Komplexen Ebene) einfach 30-50 Mal in schleife Folgende Rechnung ausführen:
Code:
x = x - f(x) / fDerivative(x)
und am ende einfach guggen, wo du landest. Mehr ist das nicht, nur stures rechnen. Nochmal in pseudocode:
Code:
f = (polynom einlesen)
fDerivative = f.derive
für jede zeile r aus dem bild{
für jeden pixel an der stelle c aus der zeile{
var x = Complex( (c / Bildbreite - 0.5) * 10, (r / Bildhöhe - 0.5) * 10 )
wiederhole 50 mal
x -= f(x)/fDerivative(x)
färbe den pixel (r,c) mit der farbe Color(127 + cos(x.im)*127, 127 + sin(x.re)*127, 0)
}
}
Nix mit gleichungssystemen auflösen hier, nur ganz stures rechnen.
Also, kurze Erläuterung:
Es ist nicht vom Mathematikern, sondern von 6 Kiddies aus 5 Gymnasien Geschrieben, und es hat entsprechendes Niveau.
Wenn irgendwo "didaktisch sinnvoll" hervorgehoben wird, dann ist es mit größter Wahrscheinlichkeit irgendein von Lehrämtlern fabrizierter Murks.
In diesem fall ist sieht's für mich nach murks aus. Wie kommen die leute auf irgendwelche Jakobi-Matrizen?
Außerdem: wenn dir der Kram mit Polynomen nicht zusagt, dann ist es genau der Richtige zeitpunkt, um auf Julia-Mengen umzuspringen: man müsste lediglich statt
x = x - f(x)/f'(x)
einfach nur
x = x*x + c
mit c = Complex(0.285, 0.01) hinschreiben, sieht auch hübsch aus, und an Polynomen erkennt man vielleicht dann doch nicht soo viel... Hm: vielleicht sparst du dir lieber wirklich die Polynome, und bastelst lieber stattdessen doch noch eine kleine GUI, wenn du die GUI's so magst Nur halt ohne viele Knöpfe, sondern mit nur einer Eingabe-Zeile und einem "draw"-Button.
Vielen lieben Dank auch für diese ausführliche Darstellung und Erklärung.
Die GUI habe ich jetzt fertig und jetzt muss ich eine komplexe zahl parsen und in deren einzelteile (real teil und imaginär teil zerlegen ... quasi eine komplexe zahl draus machen (Complex(r,i))
Java:
package spin
importscala.swing._
importscala.swing.event._
importscala.swing.Swing._
object FractalGUIextendsSimpleSwingApplication{
override def top =newMainFrame{
title ="Newton Fractale (Julia Set)"
size =new java.awt.Dimension(500,400)
location =new java.awt.Point(100,100)
visible =trueclassDrawCompextendsPanel{
preferredSize =newDimension(200,200)
override def paintComponent(g:Graphics2D){
g.setColor(java.awt.Color.GRAY)
g.fillRect(0,0, size.width, size.height)}}// button draw
val buttonDraw =newButton{ text ="Draw"}
val inputField =newTextField(10)
val inputLabel =newLabel("Plz insert a complex number: ")// view panels
val panelTop =newFlowPanel{
contents +=newDrawComp}
val panelBottom =newFlowPanel{
contents += buttonDraw
contents += inputLabel
contents += inputField
}// add to content
contents =newBorderPanel{importBorderPanel.Position._
add(panelTop,BorderPanel.Position.Center)add(panelBottom,BorderPanel.Position.South)}listenTo(buttonDraw)
reactions +={caseButtonClicked(e)=>println(inputField.text)}}}
Weiterhin habe ich alle deine Anmerkungen mir sehr zu Herzen genommen und entsprechend angepasst im Quellcode.
Du hattest mit deinem Kommentar - "Subclass???" vollkommen recht, denn dieser war unsinnig!!
GUI:
Ich hoffe dass du dir das so ungefähr vorgestellt hast. In der Klasse DrawComp werde ich dann alles zeichnen.
Ich mache denn mal weiter Kommentare sind von allen herzlich willkommen.
package spin
importscala.swing._
importscala.swing.event._
importscala.swing.Swing._
importComplex._
importjava.awt.image.BufferedImage
object FractalGUIextendsSimpleSwingApplication{var complexNumber :Complex=null
override def top =newMainFrame{
title ="Newton Fractale (Julia Set)"
size =new java.awt.Dimension(500,400)
location =new java.awt.Point(100,100)
visible =trueclassDrawCompextendsPanel{
preferredSize =newDimension(200,200)
override def paintComponent(g:Graphics2D){if(complexNumber !=null){//g.drawString(complexNumber.toString(),50,50)// image to show newton fractal
val bi =newBufferedImage(200+1,200+1,BufferedImage.TYPE_INT_RGB)var scale =100for(x <-0to200; y <-0to200){var value =Complex((x/200-0.5)*10,(y/200-0.5)*10)var count =0while(count <=50){
value += value * value + complexNumber
count +1}
bi.setRGB(127+Math.cos(value.im).toInt *127,127+Math.sin(value.re).toInt *127,0)}
g.drawImage(bi,0,0,null)}}}// button draw
val buttonDraw =newButton{ text ="Draw"}
val inputFieldReal =newTextField(10)
val inputFieldIm =newTextField(10)
val inputLabelReal =newLabel("Real Part: ")
val inputLabelIm =newLabel("Imaginary Part: ")// view panels
val panelTop =newFlowPanel{
contents +=newDrawComp}
val panelBottom =newFlowPanel{
contents += buttonDraw
contents += inputLabelReal
contents += inputFieldReal
contents += inputLabelIm
contents += inputFieldIm
}// add to content
contents =newBorderPanel{importBorderPanel.Position._
add(panelTop,BorderPanel.Position.Center)add(panelBottom,BorderPanel.Position.South)}listenTo(buttonDraw)
reactions +={caseButtonClicked(e)=>try{var real = inputFieldReal.text.toDouble
var im = inputFieldIm.text.toDouble
complexNumber = real + im *I}catch{case error:NumberFormatException=>"Number Format Exception - real input is invalid."}
repaint
}}// end main frame}
Hallo, ich schaue mir jetzt schon zum 10ten mal den Algorithmus an und ich verstehe ohne Kommentare überhaupt nichts außer dass als aller erstes ein neues Bild erstellt wird.
Ich habe jetzt zweimal alles versucht, aber das Programm hängt sich immer auf und ich muss alles schließen.
1. BufferedImage erstellen ( Bildpixel schreiben )
- R,G,B Werte liegen zwischen 0 - 255
Muss ich für jeden Pixel noch eine Complexe Zahl definieren?
2. Bild durchlaufen von oben links bis unten rechts.
3. Dann den Algo
4. Pixel setzen
5. Bild neuzeichnen.
Was habe ich falsch gemacht und kann mir nochmal jemand erkären
wieso: 127 + cos(x.im)*127
Aber irgendwie hast du da angefangen wilde "global vars" in den code zu streuen, ich hab's ein klein wenig umgepackt:
Code:
package spinsExampleProject
import scala.swing._
import scala.swing.event._
import scala.swing.Swing._
import spinsExampleProject.Complex._
import java.awt.image.BufferedImage
import math._
object FractalGUI{
def main(args: Array[String]){
println("huhu")
//*
val top = new MainFrame {
title = "Julia Set"
size = new java.awt.Dimension(500,400)
location = new java.awt.Point(100,100)
visible = true
class DrawComp extends Panel {
val resolution = 512
preferredSize = new Dimension(resolution, resolution)
protected val bi = new BufferedImage(resolution, resolution, BufferedImage.TYPE_INT_RGB)
var c: Complex = -0.835 - 0.2321 * I
calculateImage() // passiert bei initialisierung
def calculateImage(){
val w = bi.getWidth
val h = bi.getHeight
println("recalc for" + c)
for(x <- 0 until w; y <- 0 until h){
var value = Complex( (x/w.doubleValue - 0.5) * 4, (y/h.doubleValue - 0.5) * 4) // SO teilt man ints vernünftig
// geschachtelte def's hattest du auch noch nicht gezeigt, wird mal langsam Zeit
def countIterationsUntilBailout(max: Int, bailOut: Double): Int = {
var count = 0
while(count < max){
value = value * value + c // so sollt's klappen, schlag wiki seite nach
count += 1
if( value.absSq > bailOut) return(count);
}
(value.absSq / bailOut * max).intValue
}
val maxIters = 20
val iters = countIterationsUntilBailout(maxIters, 10)
// konstruktion "sin(x).toInt" macht keinen sinn: es ist fast immer 0
// und überhaupt: wie hast du denn hier rgb gesetzt? Es erwartet doch nicht (r,g,b), sondern (x,y,argb)!!
val argb =
0xFF000000 +
(127 + cos(iters/maxIters.doubleValue * 0.98 +123)*120).toInt << 24 +
(127 + sin(iters/maxIters.doubleValue * 0.4 + 89)*120).toInt << 16 +
(127 + sin(iters/maxIters.doubleValue * 7 + 23)*120).toInt
// die farbe ist von der anzahl der iterationen abhängig und mehr oder weniger zufällig
bi.setRGB(x, y, argb)
}
}
override def paintComponent(g: Graphics2D) {
g.drawImage(bi, 0, 0, bi.getWidth, bi.getHeight, null)
}
}
// button draw
val buttonDraw = new Button { text = "Draw" }
val inputFieldReal = new TextField(10)
val inputFieldIm = new TextField(10)
val inputLabelReal = new Label("Real Part: ")
val inputLabelIm = new Label("Imaginary Part: ")
// draw component
val drawComponent = new DrawComp{
listenTo(buttonDraw)
reactions += {
case ButtonClicked(e) =>
try {
val real = inputFieldReal.text.toDouble
val im = inputFieldIm.text.toDouble
c = real + im * I
calculateImage()
repaint()
} catch {
case error: NumberFormatException => c = 0 // auf default zurücksetzen, kreis malen
}
}
}
// view panels
val panelTop = new FlowPanel {
contents += drawComponent
}
val panelBottom = new FlowPanel {
contents += buttonDraw
contents += inputLabelReal
contents += inputFieldReal
contents += inputLabelIm
contents += inputFieldIm
}
// add to content
contents = new BorderPanel {
import BorderPanel.Position._
add(panelTop,BorderPanel.Position.Center)
add(panelBottom,BorderPanel.Position.South)
}
} // end main frame
//*/
}
}
Der Grund, warum du nichts gesehen hast: es ist viel zu schnell nach Unendlich abgehauen, also war alles schwarz. Die Färbe-taktik, die ich zuerst vorgeschlagen hatte, funzte nur für Newton-Fraktale, weil die eben gegen Nullstellen konvergieren, statt einfach abzuhauen.
Ich habe jetzt den code umgeändert, der vergibt die Farbe jetzt nicht nach der Distanz zum Ursprung am Ende der Iteration, sondern nach der Anzahl der schritte, nach der die Iterationsfolge aus dem Kreis mit radius sqrt(bailOut) rausspringt.
Die farben sind irgendwie voll finster geraten, kann man aber später noch abändern.
Kann's später gerne genauer erläutern, bin an diesem Wochenende erstmal voll unter Stress, sorry.
EDIT: Achso, "SimpleSSwingApplication" ist sicher tolle Sache, aber Eclipse wollte nicht, also hab ich's kurzerhand weggeworfen^^
Ich wollte es mal ausprobieren, aber ichj habe zwei Fehler beim kompilieren:
Java:
.\FractalGUI.scala:41: error: value absSq is not a member of Complexif( value.absSq > bailOut)return(count);^.\FractalGUI.scala:43: error: value absSq is not a member of Complex(value.absSq / bailOut * max).intValue
^
two errors found
Du hast sicherlich in der Klasse Complex noch eine Methode hinzugefügt.
Kannst du die noch posten? Bitte , danke.
def absSq ={
val sqLen = re * re + im * im
sqrt(sqLen)*sqrt(sqLen)}
Achso :
Java:
def countIterationsUntilBailout(max:Int, bailOut:Double):Int={var count =0while(count < max){
value = value * value + c // so sollt's klappen, schlag wiki seite nach
count +=1if( value.absSq > bailOut)return(count);}(value.absSq / bailOut * max).intValue
}
val maxIters =20
val iters =countIterationsUntilBailout(maxIters,10)// konstruktion "sin(x).toInt" macht keinen sinn: es ist fast immer 0// und überhaupt: wie hast du denn hier rgb gesetzt? Es erwartet doch nicht (r,g,b), sondern (x,y,argb)!!
val argb =0xFF000000+(127+cos(iters/maxIters.doubleValue *0.98+123)*120).toInt <<24+(127+sin(iters/maxIters.doubleValue *0.4+89)*120).toInt <<16+(127+sin(iters/maxIters.doubleValue *7+23)*120).toInt
// die farbe ist von der anzahl der iterationen abhängig und mehr oder weniger zufällig
bi.setRGB(x, y, argb)}}
Kannst du mir diesen Teil noch ein wenig genauer erkären?
Wie ist dass mit den Iterationsschritten gemeint?:rtfm:
Woher kommen die Werte die du bei der val argb benutzt?
Das shiften verstehe ich
Du ziehst doch keine Wurzel aus dem Zeug, um's gleich zu quadrieren?
Also, du hast eine Folge von komplexen Zahlen, die ganz lustig in der Ebene rumhüpft.
Du beobachtest sagen wir mal 50 Schritte. Wenn die folge nach diesen 50 schritten im Kreis vom Radius sqrt(bailOut) bleibt, dann färbst du den dazugehörigen Startpunkt zum beispiel schwarz. Ansonsten schaust du, nach wie vielen Schritten die Folge aus diesem Kreis herausspringt, und färbst den Startwert entsprechend der Anzahl der Sprünge.
Woher kommen die Werte die du bei der val argb benutzt?
Die sind irgendwie zufällig hingehackt, und nicht besonders gelungen. Ich beschreibe dir am Dienstag eine etwas schönere Strategie, hab im Moment etwas wenig Zeit, sorry. Aber ab Dienstag bin ich wieder da.