[JAVA] Eine Einführung in Funktionstypen für objektorientierte Programmierer in Elm

Dies ist der vierte Tag des Ulmen-Adventskalenders.

Der dritte Tag war @ arowMs Umgang mit speziellen Klicks mit SPA-Routing.

Ich habe das Gefühl, dass ich unendlich viele Artikel wie diesen schreibe, aber dieses Mal möchte ich Funktionstypen lernen, indem ich objektorientierte Sprachen berühre, aber diejenigen, die denken, dass sie schwer sind oder angefangen haben, Funktionstypen zu lernen. Dieser Artikel ist für diejenigen, die nicht schreiben können. Da eine objektorientierte Sprache zu weit gefasst ist, habe ich ein Java-Programm als Beispiel genommen und es mit dem Bewusstsein geschrieben, wie man es in der reinen Funktionssprache Elm ausdrückt.

Class

In einer funktionalen Sprache erfolgt die Programmierung durch Kombinieren von Funktionen und Datenstrukturen. In Java erfolgt die Programmierung durch Definieren von Klassen. Wenn Sie sich nur diese Erklärung ansehen, was ist der Unterschied zwischen dem Funktionstyp und der C-Sprache? Ich frage mich, ob objektorientiert und funktional völlig unterschiedliche Sprachen sind, aber das ist nicht der Fall. Lassen Sie uns zunächst eine einfache Klasse vorbereiten. Eine `Person``` Klasse, die Felder und einige Methoden überlädt, die in * Getter *, * Setter * und der` Object``` Klasse definiert sind. Beachten Sie, dass die Methode "always" einen Wert zurückgibt. Ist * Setter * nicht unmöglich? Sie könnten denken, dass es als ** unveränderliches Objekt ** konzipiert ist, indem Sie ein neues Objekt mit einem neuen Wertesatz zurückgeben.

Person.java

// new Person("Takeshi", 15)
  
public class Person {
    private final String name;
    private final int age;
  
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
  
    public String getName() {
        return name;
    }
  
    public Person setName(String name) {
        return new Person(name, age);
    }
  
    public int getAge() {
        return age;
    }
  
    public Person setAge(int age) {
        return new Person(name, age);
    }
      
    public String introduce() {
        String hi = "Hi! ";
        String n = "My name is " + name + "! ";
        String a = "My age is " + age + ".";
   
        return hi + n + a;
    }
  
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
  
        Person person = (Person) o;
  
        if (age != person.age) return false;
        return name != null ? name.equals(person.name) : person.name == null;
    }
    
    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }
  
    @Override
    public String toString() {
        return "Person \"" + name + "\" " + age;
    }
}

Lassen Sie uns nun die `Person``` Klasse in Elm reproduzieren. Typ Person = Personenname Alter`` `` Die Definition der Daten selbst wird in nur einer Zeile abgeschlossen. Dies wird als * Vereinigungstyp * bezeichnet und definiert die Felddefinition und den Konstruktor sowie `` toString,` `` equals, hashCode auf einmal auf der Sprachseite (Elm). Ich werde. `Person" Takeshi "15``` Sie können eine Instanz erstellen (obwohl nicht), indem Sie sie so schreiben. Name``` und Age``` sind nur Alias vom Typ String``` und `` Int```, aber nur das Hinzufügen von Alias macht die Definition sehr einfach zu sehen. (Ich möchte es, wenn ich DDD auch in Java mache).

Person.elm

module Person exposing (Person(..), getName, setName, getAge, setAge)


type alias Name =
    String


type alias Age =
    Int


{-| Person "Takeshi" 15
(Person "Takeshi" 15 |> toString) == "Person "Takeshi" 15"
-}
type Person
    = Person Name Age

In Elm wird eine Methode durch eine Funktion dargestellt. Was ist eine Methode?Eine Funktion, die Argumente und das Objekt selbst als Argumente akzeptiertKann als betrachtet werden. Die Reihenfolge der Argumente ändert sich aber nichtArgument 1->Argument 2->Argument 3... ->Objekt->RückgabetypIn Elm wird empfohlen, in dieser Reihenfolge zu definieren. Das Geheimnis ist der Rohrbetreiber(|>)Es ist in. Der Pipe-Operator kann so verwendet werden, dass das Argument der Funktion von der linken zur rechten Seite fließt.x |> f == f xPipes erleichtern das Schreiben von Code wie Methodenketten.

-- setName : Name -> Person -> Person
setName "John" (Person "Takeshi" 15 ) == Person "John" 15

-- new Person("Takeshi", 15).setName("John")Sie können es fließend lesen!
(Person "Takeshi" 15 |> setName "John") == Person "John" 15

Werfen wir einen Blick auf die Methoden (Funktionen) vom Typ `Person```. Mit * union type * geschriebene Typen können mithilfe einer Syntax namens Pattern Matching Feld für Feld zerlegt werden. Nicht verwendete Felder können mit _``` ignoriert werden. Die Syntax `` let in`` `definiert eine lokale Variable in der let-Klausel und schreibt einen Ausdruck, der den endgültigen Rückgabewert in in zurückgibt.

{-| (Person "Takeshi" 15 |> getName) == "Takeshi"
-}
getName : Person -> Name
getName (Person name _) =
    name



{-| (Person "Takeshi" 15 |> setName "John") == Person "John" 15
-}
setName : Name -> Person -> Person
setName name (Person _ age) =
    Person name age


{-| (Person "Takeshi" 15 |> getAge) == 15
-}
getAge : Person -> Age
getAge (Person _ age) =
    age


{-| (Person "Takeshi" 15 |> setAge 20) == Person "John" 20
-}
setAge : Age -> Person -> Person
setAge age (Person name _) =
    Person name age


{-| (Person "Takeshi" 15 |> introduce) == "Hi! My name is Takeshi! My age is 15."
-}
introduce : Person -> String
introduce (Person name age) =
    let
        hi =
            "Hi! "

        n =
            "My name is " ++ name ++ "! "

        a =
            "My age is " ++ toString age ++ "."
    in
        hi ++ n ++ a

Die Syntax mag etwas nervig sein, aber ist das so? Was Sie sowohl in OOP als auch in FP tun, ist dasselbe, richtig? Hast du Lust das zu sagen? Dies ist die Grundform des Denkens. Für Datenstrukturen wie Person können * Getter *, * Setter * einfacher mithilfe der vordefinierten Datensatzsyntax geschrieben werden. Die Schreibmethode ähnelt Json und kann in Form von ".field" aufgerufen werden, sodass die Schreibmethode näher an der objektorientierten Schreibmethode liegt. Es gibt auch eine Musterübereinstimmungssyntax für Datensätze.

type alias Person = { name : Name, age : Age }

--Datensatzgenerierung
takeshi : Person
takeshi = { name = "Takeshi", age = "15" }

-- Getter
> takeshi.name
"Takeshi" : String
-- Setter
> { takeshi | age = 20 }
{ name = "Takeshi", age = 20 } : { name : String, age : number }

introduce : Person -> String
introduce { name, age } =
    let
        hi =
            "Hi! "

        n =
            "My name is " ++ name ++ "! "

        a =
            "My age is " ++ toString age ++ "."
    in
        hi ++ n ++ a

Composition

Nachdem Sie die Klasse definiert haben, reproduzieren wir die Komposition. Hier ist der Beispielcode in Java. Es ist schwer, wenn ich versuche, es richtig zu schreiben (ich benutze keine Bibliothek wie Lombok, damit jeder es verstehen kann).

Foo.java

package composition;

// new Foo(5, new Bar(6, 7));

public class Foo {
    private final int x;
    private final Bar bar;

    public Foo(int x, Bar bar) {
        this.x = x;
        this.bar = bar;
    }

    public int getX() {
        return x;
    }

    public Foo setX(int x) {
        return new Foo(x, bar);
    }

    public int getY() {
        return bar.getY();
    }

    public Foo setY(int y) {
        return new Foo(x, bar.setY(y));
    }

    public int getZ() {
        return bar.getZ();
    }

    public Foo setZ(int z) {
        return new Foo(x, bar.setZ(z));
    }


    public Bar getBar() {
        return bar;
    }

    public int calc() {
        return x + bar.getY() + bar.getZ();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Foo foo = (Foo) o;

        if (x != foo.x) return false;
        return bar != null ? bar.equals(foo.bar) : foo.bar == null;
    }

    @Override
    public int hashCode() {
        int result = x;
        result = 31 * result + (bar != null ? bar.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        return "Foo " + x + " (" + bar + ')';
    }
}

Bar.java

package composition;

public class Bar {
    private final int y;
    private final int z;

    public Bar(int y, int z) {
        this.y = y;
        this.z = z;
    }

    public int getY() {
        return y;
    }

    public Bar setY(int y) {
        return new Bar(y, z);
    }

    public int getZ() {
        return z;
    }

    public Bar setZ(int z) {
        return new Bar(y, z);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Bar bar = (Bar) o;

        if (y != bar.y) return false;
        return z == bar.z;
    }

    @Override
    public int hashCode() {
        int result = y;
        result = 31 * result + z;
        return result;
    }

    @Override
    public String toString() {
        return "Bar " + y + ' ' + z;
    }
}

Dies entspricht im Wesentlichen dem in der Klasse erläuterten Inhalt. Bringen Sie einfach den Typ "Bar" auf das Feld. Die Musterübereinstimmung kann die Struktur problemlos zerlegen, selbst wenn * Vereinigungstyp * verschachtelt ist.

module Foo exposing (..)

import Bar exposing (..)


{-| Foo 5 (Bar 6 7)
-}
type Foo
    = Foo Int Bar


getX : Foo -> Int
getX (Foo x _) =
    x


setX : Foo -> Int -> Foo
setX (Foo _ bar) x =
    Foo x bar


getY : Foo -> Int
getY (Foo _ (Bar y _)) =
    y


setY : Foo -> Int -> Foo
setY (Foo x (Bar _ z)) y =
    Foo x (Bar y z)


getZ : Foo -> Int
getZ (Foo _ (Bar _ z)) =
    z


setZ : Foo -> Int -> Foo
setZ (Foo x (Bar y _)) z =
    Foo x (Bar y z)


{-| (Foo 5 (Bar 6 7) |> calc) == 18
-}
calc : Foo -> Int
calc (Foo x (Bar y z)) =
    x + y + z

Die Struktur kann durch Mustervergleich transparent gehandhabt werden, und der Typ "Balken" auf der zu komponierenden Seite hat keine Funktion. Wenn die Struktur und Verwendung kompliziert sind, ist es natürlich besser, die Verantwortung für die Funktion für jedes Modul wie im Java-Beispiel zu teilen. Es ist auch möglich, unter Berücksichtigung der Kapselung zu codieren. Sie können Datensätze wie oben in Klasse beschrieben verschachteln (obwohl das Aktualisieren etwas komplizierter ist). Die Freiheit, hier herum zu schreiben, ist genau die gleiche wie bei Java (ich war überrascht, als ich herausfand, dass ich es mit dem gleichen Gefühl tun konnte).

module Bar exposing (..)


type Bar
    = Bar Int Int

Polymophism

Ich glaube, ich konnte verstehen, wie man Klassen im Elm-Stil erstellt und wie man Module durch Komposition verwendet. Natürlich glaube ich nicht, dass ich damit allein zufrieden bin. Lassen Sie uns den Polymorphismus über die Schnittstelle mit Elm reproduzieren. Verwenden wir als Beispielcode die Baum- und Besuchermuster, die einfache Berechnungen durchführen. Um das Beispiel zu vereinfachen, unterstützt die Formel nur Zahlen und Additionen.

NodeTest.java

package polymorphism;

public class NodeTest {
    public static void main(String[] args) {
        Node tree = new AddNode(
                new AddNode(
                        new NumNode(2),
                        new NumNode(3)),
                new NumNode(4)
        );
        // ((2 + 3) + 4) = 9
        System.out.println(tree.accept(new Calculator()));
    }
}

Node.java

package polymorphism;

public interface Node {
    int accept(Visitor visitor);
}

NumNode.java


package polymorphism;

public class NumNode implements Node {
    private final int value;

    public NumNode(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    @Override
    public int accept(Visitor visitor) {
        return visitor.visit(this);
    }
}

AddNode.java

package polymorphism;

public class AddNode implements Node {
    private final Node left;
    private final Node right;

    public Node getLeft() {
        return left;
    }

    public Node getRight() {
        return right;
    }

    public AddNode(Node left, Node right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int accept(Visitor visitor) {
        return visitor.visit(this);
    }
}

Visitor.java

package polymorphism;

public interface Visitor {
    int visit(NumNode node);

    int visit(AddNode node);
}

Calculator.java

package polymorphism;

public class Calculator implements Visitor {
    @Override
    public int visit(NumNode node) {
        return node.getValue();
    }

    @Override
    public int visit(AddNode node) {
        int left = node.getLeft().accept(this);
        int right = node.getRight().accept(this);

        return left + right;
    }
}

Elm ist wirklich gut in Code, der Polymorphismus wie das Besuchermuster voll ausnutzt. Der * Union-Typ *, den ich bisher verwendet habe, aber durch die Verwendung der direkten Summe der Typen zeigt er seinen wahren Charakter. `Typ Node = Num Int | Add Node Node `Node``` Typ besteht aus zwei Typen, Num``` und Add``` und weiter Add``` hat eine rekursive Struktur mit zwei eigenen Node```-Typen. Das Formular selbst ist das gleiche wie der Java-Typ "Node", wird jedoch anders verwendet. Werfen wir einen Blick auf die Funktion `` calc. Sie können die neue Syntax `` `case node of`` `sehen. Dies ist eine Art Musterübereinstimmung und wird geschrieben, wenn sie eher als Ausdruck als als Argument verwendet wird. Es wird verwendet, um den direkten Summentyp * Vereinigungstyp * zu klassifizieren. Aus einem anderen Blickwinkel ist ersichtlich, dass der Typ `` `Num und der Typ`` Add``` eine Schnittstelle mit der Methode ``calc``` implementieren. Außerdem können Sie im Fall des Typs "Hinzufügen" sehen, dass "calc" rekursiv aufgerufen wird, um die Werte auf der rechten und linken Seite zu finden. Der Baum kann intuitiv durch * Vereinigungstyp * dargestellt werden.

Node.elm

module Node exposing (..)

import Html exposing (..)


type Node
    = Num Int
    | Add Node Node


calc : Node -> Int
calc node =
    case node of
        Num n ->
            n

        Add l r ->
            let
                lValue =
                    calc l

                rValue =
                    calc r
            in
                lValue + rValue


main : Html msg
main =
    let
        tree =
            Add
                (Add
                    (Num 2)
                    (Num 3)
                )
                (Num 4)
    in
        text <| toString <| calc tree

Es mag etwas gewöhnungsbedürftig sein, aber im Grunde können Sie mit einer Funktion, die * Vereinigungstyp * + Mustervergleich verwendet, problemlos reproduzieren, was Sie objektorientiert getan haben. Es gibt viele Fälle, in denen Sie sehr präzise schreiben können. Lassen Sie es uns also beherrschen.

Collection

Vergleichen wir die Sammlungen mit jshell und Java9. Die Ausführungsergebnisse werden ebenfalls angezeigt. Bitte spüren Sie den Unterschied.

List

List.java

jshell> IntStream.rangeClosed(1, 10).mapToObj(n -> n * 2).collect(Collectors.toList())
$1 ==> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
jshell> IntStream.rangeClosed(1, 10).filter(n -> n % 2 == 0).boxed().collect(Collectors.toList())
$2 ==> [2, 4, 6, 8, 10]
jshell> List<Integer> list = IntStream.rangeClosed(1, 10).boxed().collect(Collectors.toList())
$3 ==> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
jshell> Collections.reverse($3)
jshell> $3
$3 ==> [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
jshell> IntStream.rangeClosed(1, 10).limit(5).boxed().collect(Collectors.toList())
$4 ==> [1, 2, 3, 4, 5]
jshell> IntStream.rangeClosed(1, 10).skip(5).boxed().collect(Collectors.toList()
$5 ==> [6, 7, 8, 9, 10]
jshell> Stream.generate(() -> "a").limit(10).collect(Collectors.toList())
$6 ==> [a, a, a, a, a, a, a, a, a, a]
Stream.concat(Stream.concat(Stream.of(1, 2), Stream.of(3)), Stream.of(4, 5)).collect(Collectors.toList())
$7 ==> [1, 2, 3, 4, 5]
jshell> IntStream.rangeClosed(1, 5).flatMap(i -> IntStream.of(0, i)).boxed().skip(1).collect(Collectors.toList())
$8 ==> [1, 0, 2, 0, 3, 0, 4, 0, 5]
jshell> IntStream.rangeClosed(1, 10).boxed().collect(Collectors.partitioningBy(n -> n % 2 == 0))
$9 ==> {false=[1, 3, 5, 7, 9], true=[2, 4, 6, 8, 10]}
jshell> String[] alphas = {"a", "b", "c", "d", "e"}
alphas ==> String[5] { "a", "b", "c", "d", "e" }
jshell> IntStream.range(0, 5).boxed().collect(Collectors.toMap(i -> i, i -> alphas[i])).entrySet()
$10 ==> [0=a, 1=b, 2=c, 3=d, 4=e]

List.elm

> List.range 1 10 |> List.map (\n -> n * 2)
[2,4,6,8,10,12,14,16,18,20] : List Int
> List.range 1 10 |> List.filter (\n -> n % 2 == 0)
[2,4,6,8,10] : List Int
> List.range 1 10 |> List.reverse
[10,9,8,7,6,5,4,3,2,1] : List Int
> List.range 1 10 |> List.take 5
[1,2,3,4,5] : List Int
> List.range 1 10 |> List.drop 5
[6,7,8,9,10] : List Int
> List.repeat 10 "a"
["a","a","a","a","a","a","a","a","a","a"] : List String
> List.concat [[1,2],[3],[4,5]]
[1,2,3,4,5] : List number
> List.range 1 5 |> List.intersperse 0
[1,0,2,0,3,0,4,0,5] : List Int
> List.range 1 10 |> List.partition (\n -> n % 2 == 0)
([2,4,6,8,10],[1,3,5,7,9]) : ( List Int, List Int )
> List.indexedMap (,) ["a","b","c","d","e"]
[(0,"a"),(1,"b"),(2,"c"),(3,"d"),(4,"e")] : List ( Int, String )

Map

Map.java

jshell> Map.of("a", 1, "b", 2, "c", 3)
$1 ==> {b=2, c=3, a=1}
jshell> Map.of("a", 1, "b", 2, "c", 3).keySet()
$2 ==> [c, b, a]
jshell> Map.of("a", 1, "b", 2, "c", 3).values()
$3 ==> [3, 2, 1]
jshell> Map.of("a", 1, "b", 2, "c", 3).containsKey("a")
$4 ==> true
jshell> Map.of("a", 1, "b", 2, "c", 3).get("a")
$5 ==> 1
jshell> Map.of("a", 1, "b", 2, "c", 3).get("d")
$6 ==> null
jshell> Map.of("a", 1, "b", 2, "c", 3).getOrDefault("d", -1)
$7 ==> -1
jshell> HashMap<String, Integer> hash = new HashMap<>(Map.of("a", 1, "b", 2, "c", 3)))
hash ==> {a=1, b=2, c=3}
jshell> hash.put("d", 4)
$8 ==> null
jshell> hash.get("d")
$9 ==> 4
jshell> HashMap<String, Integer> hash = new HashMap<>(Map.of("a", 1, "b", 2, "c", 3))
hash ==> {a=1, b=2, c=3}
jshell> hash.replace("a", hash.get("a") + 10)
$10 ==> 1
jshell> hash
hash ==> {a=11, b=2, c=3}
jshell> HashMap<String, Integer> hash = new HashMap<>(Map.of("a", 1, "b", 2, "c", 3))
hash ==> {a=1, b=2, c=3}
jshell> hash.remove("a")
$35 ==> 1
jshell> hash
hash ==> {b=2, c=3}
//Ab diesem Zeitpunkt aufgegeben

Map.elm

> Dict.fromList [("a",1),("b",2),("c",3)]
Dict.fromList [("a",1),("b",2),("c",3)] : Dict.Dict String number
> Dict.fromList [("a",1),("b",2),("c",3)] |> Dict.keys
["a","b","c"] : List String
> Dict.fromList [("a",1),("b",2),("c",3)] |> Dict.values
[1,2,3] : List number
> Dict.fromList [("a",1),("b",2),("c",3)] |> Dict.member "a"
True : Bool
> Dict.fromList [("a",1),("b",2),("c",3)] |> Dict.get "a"
Just 1 : Maybe.Maybe number
> Dict.fromList [("a",1),("b",2),("c",3)] |> Dict.get "d"
  Nothing : Maybe.Maybe number
> Dict.fromList [("a",1),("b",2),("c",3)] |> Dict.get "d" |> Maybe.withDefault -1
-1 : number
> Dict.fromList [("a",1),("b",2),("c",3)] |> Dict.insert "d" 4 |> Dict.get "d"
Just 4 : Maybe.Maybe number
> Dict.fromList [("a",1),("b",2),("c",3)] |> Dict.update "a" (\m -> Maybe.map (\v -> v + 10) m)
> Dict.fromList [("a",1),("b",2),("c",3)] |> Dict.update "a" (Maybe.map (\v -> v + 10))
> Dict.fromList [("a",1),("b",2),("c",3)] |> Dict.update "a" (Maybe.map ((+) 10))
Dict.fromList [("a",11),("b",2),("c",3)] : Dict.Dict String number
> Dict.fromList [("a",1),("b",2),("c",3)] |> Dict.remove "a"
Dict.fromList [("b",2),("c",3)] : Dict.Dict String number
--Ulme nur von hier
> Dict.fromList [("a",1),("b",2),("c",3)] |> Dict.map (\k v -> k ++ toString v)
Dict.fromList [("a","a1"),("b","b2"),("c","c3")] : Dict.Dict String String
> Dict.fromList [("a",1),("b",2),("c",3)] |> Dict.foldl (\k v z -> v + z) 0
6 : number
> Dict.fromList [("a",1),("b",2),("c",3)] |> Dict.filter (\k v -> v >= 2)
Dict.fromList [("b",2),("c",3)] : Dict.Dict String number
> Dict.union (Dict.fromList [("a",1),("b",2),("c",3)]) (Dict.fromList [("d",4)])
Dict.fromList [("a",1),("b",2),("c",3),("d",4)] : Dict.Dict String number
> Dict.diff (Dict.fromList [("a",1),("b",2),("c",3)]) (Dict.fromList [("a",1)])
Dict.fromList [("b",2),("c",3)] : Dict.Dict String number

Zusammenfassung

Für diejenigen, die zögern, mit funktionalen Sprachen zu beginnen, oder die unter der Lücke zu objektorientierten Sprachen leiden, gibt es keinen Unterschied, was sie tun möchten! Ich wollte, dass Sie das bemerken und fasste es im Artikel zusammen. Außerdem wollte ich, dass Sie die zahlreichen Sammlungen und Funktionen für Sammlungsvorgänge als Merkmal der Funktionssprache ansprechen, und habe daher die Ausführungsbeispiele kurz angeordnet. Ich wünsche Ihnen viel Spaß beim Einstieg in den Funktionstyp!

Recommended Posts

Eine Einführung in Funktionstypen für objektorientierte Programmierer in Elm
Einführung in die funktionale Programmierung (Java, Javascript)
Einführung in die Programmierung für Studenten: Einführung
Einführung in die Programmierung für Studenten: Variablen
Erste Schritte mit Groovy für problematische Java-Ingenieure
Einführung in die praktische Programmierung
Einführung in die Programmierung für Studenten (von Zeit zu Zeit aktualisiert)
Einführung in die Programmierung für Studenten: Erstellen von Canvas
~ Ich habe jetzt versucht, funktionale Programmierung mit Java zu lernen ~
Was ist schließlich Objektorientierung oder nur eine Sache, auf die Sie bei der Programmierung achten müssen?
Einführung in die Programmierung für Studenten: Vorbereitung Installieren wir die Verarbeitung
Einführung in die Programmierung für Studenten: Zeichnen grundlegender Quadrate
Einführung in die Programmierung für Studenten: Zeichnen Sie gerade Linien
Einführung in die Programmierung für Studenten: Machen Sie gerade Linien attraktiver
Verstehen Sie, wie die funktionale Programmierung in Java auf einen Schlag eingeführt wurde
Einführung in die Programmierung für Studenten: Verschiedene Funktionen in Bezug auf Quadrate (Teil 1)
Wie man die Programmierung in 3 Monaten beherrscht
Einführung in Kotlin für iOS-Entwickler lin ー Kotlin-Erstellung
Einführung in Kotlin für iOS-Entwickler ④-Typ
Testgetriebene Entwicklung mit der Funktionssprache Elm
[Für Anfänger] So debuggen Sie mit Eclipse
Für meinen Sohn, der angefangen hat, Java mit "Einführung in Java" in einer Hand zu studieren
So erstellen Sie eine Beurteilungsmethode, um nach einem beliebigen Zeichen im Array zu suchen