[JAVA] Versuchen Sie, ein Zusatzprogramm in mehreren Sprachen zu erstellen

Einführung

Zuerst habe ich die objektorientierte Programmierung und ihre Funktionen zusammengefasst, aber ich konnte es nicht gut machen und gab auf.

Stattdessen dachte ich, es wäre besser, eine einfache Aufgabe zu erstellen, sie konkret zu programmieren und zu vergleichen, und entschied mich daher, mit einem einfachen Additionsprogramm zu beginnen.

Als Sprache habe ich Pascal (Standard-Pascal) als prozedurorientierte Sprache, Java als objektorientierte Sprache und Haskell als funktionale Sprache gewählt.

Diese Sprachwahl ist ein Hobby. Ich kann es nicht gut sagen, aber ich mag eine Sprache, die ein einziges Paradigma und statische Typen hat, die eine lange Syntax hat und die sich ein bisschen "matschig" anfühlt.

Die Addition berechnet nicht nur in einer Zeile, sondern akzeptiert Eingaben des Benutzers und definiert die Funktion wie folgt.

1.Zunächst wird die Meldung "Geben Sie eine Nummer ein. Sie endet mit einer Leerzeile." Angezeigt.
2. 「>Wird angezeigt und wartet auf Eingabe. Wenn Sie eine Zahl eingeben und eine Zeile unterbrechen, wird diese aufgezeichnet
3.nochmal">Wird angezeigt und wartet auf Eingabe. Wiederholen Sie diesen Vorgang bis zu einem Zeilenumbruch, ohne Zeichen einzugeben.
4.Wenn ein Zeilenumbruch auftritt, wird das Ergebnis der Addition der Summe aller bisher eingegebenen Zahlen angezeigt. "Die Summe ist xxx" wird angezeigt.
5.Ignorieren Sie Eingaben, die andere Zeichen als Zahlen enthalten.

Eigentlich denke ich, dass ein etwas komplizierteres Beispiel die Individualität jeder Sprache hervorheben wird, aber der erste Schritt ist das.

Das tatsächliche Betriebsbild ist wie folgt.

Bitte geben Sie die Nummer ein. Es endet mit einer Leerzeile.
>10
>20
>4
>
Die Summe beträgt 34.

Dieser Artikel richtet sich an Anfänger, die eine Programmiersprache gelernt haben und die bedingte Verzweigung und Schleifenverarbeitung verstehen.

Implementierung in Pascal

Betrachten wir hier Standard-Pascal. Standard Pascal hat nur wenige Funktionen und kann nicht aufgeteilt werden, aber ich denke, es ist immer noch zum Erlernen der Programmierung geeignet, insbesondere für die Grundlagen von Lernalgorithmen.

Betrachten Sie zunächst die Implementierung der Hauptroutine wie folgt:


program CalcSumApp;

type
  ItemPtr = ^Item;
  Item = 
    record
      next: ItemPtr;
      text: ShortString;
    end;

//Definition verschiedener Funktionen

var
  lines : ItemPtr;
  sum : Integer;

begin
  writeln('Bitte geben Sie die Nummer ein. Es endet mit einer Leerzeile.');
  lines := readLines();
  sum := sumOfLines(lines);
  writeln('Die Summe ist' + toStr(sum) + 'ist.');
  disposeAll(lines);
end.

Zuerst definieren wir einen Zeiger namens ItemPtr. Tatsächlich kann in Standard-Pascal (ISO 7185 Pascal) kein Array mit variabler Länge verwendet werden. Daher definieren wir hier einen Knoten, der nur einen numerischen Wert namens Item speichern und in Form eines Zeigers eingeben kann. Geändert, um den numerischen Wert zu speichern.

Die Hauptroutine (der Teil unter begin) sieht folgendermaßen aus:

Die Nachricht wird in der ersten Zeile angezeigt und der numerische Wert (mehrere Zeilen) wird in der zweiten Zeile akzeptiert. In der 3. Zeile wird dann die Summe der eingegebenen Zahlenwerte berechnet und in der 4. Zeile das Ergebnis angezeigt. toStr ist eine Funktion, die eine Ganzzahl in eine Zeichenfolge konvertiert. Dies ist jedoch auch nicht im Standard-Pascal enthalten und wird daher im Programm implementiert. Die fünfte Zeile gibt Speicher frei. Sie müssen den Zeiger selbst loslassen, wenn Sie fertig sind.

Betrachten wir nun die Implementierung von "readLines" im Eingabeteil.


function readLines(): ItemPtr;
var 
  result: ItemPtr;
  prevItemPtr: ItemPtr;
  currItemPtr: ItemPtr;
  line: ShortString;
begin
  result := nil;
  prevItemPtr := nil;
  while true do
  begin
    write('>');
    readln(line);
    if line <> '' then 
    begin
      if prevItemPtr = nil then
      begin
        new(prevItemPtr);
        prevItemPtr^.text := line;
        prevItemPtr^.next := nil;
        result := prevItemPtr;
      end
      else 
      begin
        new(currItemPtr);
        currItemPtr^.text := line;
        currItemPtr^.next := nil;
        prevItemPtr^.next := currItemPtr;
        prevItemPtr := currItemPtr;
      end;
    end   
    else
      break;
  end;
  readLines := result;
end;

Schließen Sie das Ganze in eine while-Schleife ein und machen Sie es zu einem Format, das wiederholt wird, bis eine Bedingung (Eingabe eines leeren Zeilenvorschubs) erfüllt ist.

readln (line) ist der Teil, der die Zeicheneingabe empfängt.

Nur das erste Mal ist etwas Besonderes und behält den eingegebenen Wert in prevItemPtr bei. new (prevItemPtr) ist eine Anweisung, einen Bereich im Heapspeicher zuzuweisen, den eingegebenen numerischen Wert dort zu speichern und im Ergebnis zu speichern (result). Ab dem zweiten Mal bleibt der eingegebene Wert in "currItemPtr" erhalten. Dann wird der eingegebene numerische Wert gespeichert und aus dem vorherigen "prevItemPtr" referenziert. Zum Schluss aktualisieren Sie currItemPtr als neues prevItemPtr.

Als nächstes folgt die Funktion sumOfLines, die die Addition berechnet.

function sumOfLines(lines: ItemPtr): Integer;
var
  a : Integer;
  v : Integer;
  err: Boolean;
begin
  a := 0;
  while lines <> nil do
  begin
    err := false;
    v := toInt(lines^.text, err);
    if not err then
      a := a + v;
    lines := lines^.next;
  end;
  sumOfLines := a;
end;

Dadurch wird die Zeichenfolge in eine Ganzzahl konvertiert und hinzugefügt, während die Zeilen Zeile für Zeile überprüft werden. Die toInt-Funktion, die eine Zeichenfolge in eine Ganzzahl konvertiert, wird auch im Programm definiert, da sie nicht im Standard-Pascal enthalten ist. Wenn eine andere Zeichenfolge als ein numerischer Wert eintrifft, wird dies anhand des zweiten Arguments err beurteilt, um keinen Fehler zu verursachen. (Daher ist dieses zweite Argument ein Referenzaufruf.)

Das ganze Programm sieht so aus:

program CalcSumApp;

type
  ItemPtr = ^Item;
  Item = 
    record
      next: ItemPtr;
      text: ShortString;
    end;

function toInt(str: ShortString; var err: Boolean): Integer;
var
  i : Integer;
  n : Integer;
  r : Integer;
begin
  r := 0;
  for i := 1 to ord(str[0]) do
  begin
    n := Ord(str[i]);
    if (n < Ord('0')) or (n > Ord('9')) then 
      begin
        err := true;
        break;
      end;
    r := r * 10 + n - Ord('0');
  end;
  toInt := r;
end;

function toStr(n: Integer): ShortString;
var
  m : Integer;
  s : ShortString;
begin
  s := '';
  while n > 0 do
  begin
    m := n mod 10;
    n := n div 10;
    s := chr(m + ord('0')) + s;
  end;
  if s = '' then
    s := '0';
  toStr := s;
end;

procedure disposeAll(ptr: ItemPtr);
begin
  if ptr <> nil then
  begin
    disposeAll(ptr^.next);
  end;
end;

function readLines(): ItemPtr;
var 
  result: ItemPtr;
  prevItemPtr: ItemPtr;
  currItemPtr: ItemPtr;
  line: ShortString;
begin
  result := nil;
  prevItemPtr := nil;
  while true do
  begin
    write('>');
    readln(line);
    if line <> '' then 
    begin
      if prevItemPtr = nil then
      begin
        new(prevItemPtr);
        prevItemPtr^.text := line;
        prevItemPtr^.next := nil;
        result := prevItemPtr;
      end
      else 
      begin
        new(currItemPtr);
        currItemPtr^.text := line;
        currItemPtr^.next := nil;
        prevItemPtr^.next := currItemPtr;
        prevItemPtr := currItemPtr;
      end;
    end   
    else
      break;
  end;
  readLines := result;
end;

function sumOfLines(lines: ItemPtr): Integer;
var
  a : Integer;
  v : Integer;
  err: Boolean;
begin
  a := 0;
  while lines <> nil do
  begin
    err := false;
    v := toInt(lines^.text, err);
    if not err then
      a := a + v;
    lines := lines^.next;
  end;
  sumOfLines := a;
end;

var
  lines : ItemPtr;
  sum : Integer;

begin
  writeln('Bitte geben Sie die Nummer ein. Es endet mit einer Leerzeile.');
  lines := readLines();
  sum := sumOfLines(lines);
  writeln('Die Summe ist' + toStr(sum) + 'ist.');
  disposeAll(lines);
end.

Implementierung in Java

In Java implementieren wir es mit zwei Klassen, der CalcSumApp-Klasse und der CalcSum-Klasse. Die erste ist die CalcSumApp-Klasse für die gesamte Anwendung.


public class CalcSumApp {
  public static void main(String args[]) {
    CalcSum cs = new CalcSum();
    CalcSumApp app = new CalcSumApp();
    app.start(cs);
  } 

  public void start(CalcSum cs) {
    System.out.println("Bitte geben Sie die Nummer ein. Es endet mit einer Leerzeile.");
    cs.readLines();
    int sum = cs.getSum();
    System.out.println("Die Summe ist" + String.valueOf(sum) + "ist.");
  }
}

Erstellen Sie in der Funktion "main" eine Instanz der Klasse "CalcSumApp" und der Klasse "calcSum" und rufen Sie die Methode "start" auf.

Der Inhalt der Methode "start" entspricht fast der Hauptroutine von Pascal, verarbeitet jedoch keine Daten direkt, sondern nur Objekte der Klasse "CalcSum".

Dann sieht die CalcSum-Klasse folgendermaßen aus:

class CalcSum {
  List<String> list;

  CalcSum () {
    list = new ArrayList<String>();
  }

  public void readLines() {
    BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
    String line;
    try {
      do {
        System.out.print('>');
        line = input.readLine();
        if ("".equals(line)) {
          break;
        }
        list.add(line);
      } while (true);
    } catch (IOException e) {

    }
  }

  public int getSum() { 
    int sum = 0;
    for (String s : list) {
      try {
        sum += Integer.valueOf(s).intValue();
      } catch (NumberFormatException e) {

      }
    }
    return sum;
  }
}

Die Methode "readLines", die Eingaben von der Konsole akzeptiert, und die Methode "getSum", die die Summe berechnet, sind definiert. Der Inhalt entspricht fast der Funktion "readLines" und der Funktion "sumOfLines" in pascal. Die eingegebenen numerischen Daten (Liste) bleiben jedoch als Instanzvariable der Klasse "CalcSum" erhalten.

Das ganze Programm sieht so aus: Im Vergleich zu Pascal ist es sauberer, da es keinen Konvertierungsprozess wie toInt oder toStr oder einen Prozess wie disposeAll of memory release gibt und es schön ist, dass die Liste in CalcSum passt. (Obwohl es vielleicht besser ist, es "privat" zu machen)

import java.util.Scanner;
import java.util.List;
import java.util.ArrayList;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.lang.NumberFormatException;

class CalcSum {
  List<String> list;

  CalcSum () {
    list = new ArrayList<String>();
  }

  public void readLines() {
    BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
    String line;
    try {
      do {
        System.out.print('>');
        line = input.readLine();
        if ("".equals(line)) {
          break;
        }
        list.add(line);
      } while (true);
    } catch (IOException e) {

    }
  }

  public int getSum() {
    int sum = 0;
    for (String s : list) {
      try {
        sum += Integer.valueOf(s).intValue();
      } catch (NumberFormatException e) {

      }
    }
    return sum;
  }
}

public class CalcSumApp {
  public static void main(String args[]) {
    CalcSum cs = new CalcSum();
    CalcSumApp app = new CalcSumApp();
    app.start(cs);
  } 

  public void start(CalcSum cs) {
    System.out.println("Bitte geben Sie die Nummer ein. Es endet mit einer Leerzeile.");
    cs.readLines();
    int sum = cs.getSum();
    System.out.println("Die Summe ist" + String.valueOf(sum) + "ist.");
  }
}

Da dieses Beispiel einfach ist, denke ich, dass es möglich ist, CalcSum als unveränderliches Objekt zu implementieren, aber im Allgemeinen denke ich, dass bei der objektorientierten Programmierung die Objekte, die unveränderlich sein können, begrenzt sind, sodass ich nicht auf die Implementierung eingehen werde, die es unveränderlich macht. tat.

Implementierung in Haskell

Bei Haskell sieht die Hauptroutine ziemlich gleich aus und sieht folgendermaßen aus:

  main :: IO ()
  main = do
    hSetBuffering stdout NoBuffering
    putStrLn "Bitte geben Sie die Nummer ein. Es endet mit einer Leerzeile."
    lines <- readLines getLines
    list <- sequence lines
    let s = sum list
    putStrLn $ "Die Summe ist" ++ (show s) ++ "ist."

Der in den Kommentaren in diesem Artikel erwähnte Teil "hSetBuffering stdout NoBuffering" war, dass die Pufferung ohne ihn nicht garantieren würde, dass die Ein- und Ausgänge in der erwarteten Reihenfolge sind. (Danke an @igrep)

getLines ist eine Liste, die unendlich viele Zeichenfolgen empfängt und wie folgt definiert ist.


  getLines :: [IO String]
  getLines = do
    repeat $ putStr ">" >> getLine

repeat erstellt eine unendliche Liste. Mit anderen Worten

repeat x = [x x x x .... x ... ]

Es ist wie es ist. Es ist ein Merkmal von Hakells Verzögerungsverarbeitung, dass eine solche unendliche Liste verarbeitet werden kann, und ich wollte sie so verwenden.

readLines ist der Vorgang des Ausschneidens des Teils, bis eine leere Zeile eingegeben wird, und ist wie folgt.


  readLines :: [IO String] -> IO [IO Int]
  readLines [] = return []
  readLines (l:ls) = do
    v <- l :: IO String
    if v == ""
      then return []
    else do
      ll <- readLines ls
      case readMaybe v of 
        Just x -> return $ (return x):ll
        Nothing -> return ll

Wenn das erste Element des Arrays ein leeres Zeichen ist, ist es das. Andernfalls, Konvertieren Sie das erste Element in einen numerischen Wert (readMaybe). Wenn es konvertiert werden kann, fügen Sie das konvertierte hinzu (nachverarbeitet) und geben Sie es zurück. Wenn es nicht konvertiert werden kann, konvertieren Sie den Rest (rekursiv verarbeitet). Kommt nur zurück.

  list <- sequence lines 

Hier ist es etwas verwirrend, aber ich konvertiere ein E / A-Array in ein E / A-Array und extrahiere dessen Inhalt. "Liste" ist also nur ein Array von Zahlen.

Ursprünglich hier

  list <- mapM (>>= return) lines

Wie im Kommentar erwähnt, kann es jedoch in einem Schuss mit "squence" gelöst werden, also habe ich es geändert.

Als nächstes erfolgt die Berechnung der Gesamtsumme.

   let s = sum list

(Ich werde die Geschichte der Bewertung weglassen, weil mein Verständnis falsch war.)

Das ganze Programm sieht so aus: Die Konvertierung von Monaden ist etwas mühsam und im Gegensatz zu Java wird die Variable lines direkt behandelt, aber mit der do-Syntax kann sie wie pascal beschrieben werden.

module CalcSumApp where

  import Text.Read
  import System.IO
          
  getLines :: [IO String]
  getLines = do
    repeat $ putStr ">" >> getLine
    
  readLines :: [IO String] -> IO [IO Int]
  readLines [] = return []
  readLines (l:ls) = do
    v <- l :: IO String
    if v == ""
      then return []
    else do
      ll <- readLines ls
      case readMaybe v of 
        Just x -> return $ (return x):ll
        Nothing -> return ll
  
  main :: IO ()
  main = do
    hSetBuffering stdout NoBuffering
    putStrLn "Bitte geben Sie die Nummer ein. Es endet mit einer Leerzeile."
    lines <- readLines getLines
    list <- sequence lines
    let s = sum list
    putStrLn $ "Die Summe ist" ++ (show s) ++ "ist."

Bonus Haskell Teil 2

Haskell versuchte auch, einen Datentyp zu erstellen und ihn objektorientiert zu kapseln. Ich denke nicht, dass es praktisch ist, aber ...

Die Hauptroutine ist


  main :: IO ()
  main = do
    hSetBuffering stdout NoBuffering
    putStrLn "Bitte geben Sie die Nummer ein. Es endet mit einer Leerzeile."
    calcSum <- newCalcSum   -- calcSum = new CalcSum()
    inputStrings calcSum    -- calcSum.inputString()
    sum <- getSum calcSum   -- sum = calcSum.getSum()
    putStrLn $ "Die Summe ist" ++ (show sum) ++ "ist."

Es sieht so aus und ich denke, es ist näher an Java. Im ganzen Code


module CalcSumApp2 where
  import Text.Read
  import Control.Monad
  import Data.IORef
  import Data.Maybe
  import System.IO

  data CalcSum = CalcSum { getStrings :: IORef [String] }

  newCalcSum :: IO CalcSum
  newCalcSum = do
    ref <- newIORef []
    return $ CalcSum ref
  
  inputStrings :: CalcSum -> IO ()
  inputStrings calcSum = do
    let truncateStrings :: [IO String] -> IO [String]
        truncateStrings [] = return []
        truncateStrings (x:xs) = do
        s <- x
        if s == "" 
          then 
            return []
          else do
            l <- truncateStrings xs
            return $ (:l) $! s            -- strict evaluation of s corresponding to `return (s:l)`

    list <- truncateStrings $ repeat $ putStr ">" >> getLine
    writeIORef (getStrings calcSum) list  -- calcSums.strings = list

  getSum :: CalcSum -> IO Int
  getSum calcSum = do
    list <- readIORef (getStrings calcSum) :: IO [String] -- list = calcSum.strings 
    let nums = catMaybes $ readMaybe <$> list :: [Int] 
    return $ sum nums

  main :: IO ()
  main = do
    hSetBuffering stdout NoBuffering
    putStrLn "Bitte geben Sie die Nummer ein. Es endet mit einer Leerzeile."
    calcSum <- newCalcSum   -- calcSum = new CalcSum()
    inputStrings calcSum    -- calcSum.inputString()
    sum <- getSum calcSum   -- sum = calcSum.getSum()
    putStrLn $ "Die Summe ist" ++ (show sum) ++ "ist."
  

Was ich hier sagen wollte ist, dass das Objekt (wie das Ding) "IORef" ist. Ich denke, dass Objekte in erster Linie nichts mit E / A zu tun haben, aber in meinen Augen sind Objekte Dinge, die einen Bereich im Heapspeicher zum Lesen und Schreiben (ein Bild wie DB) getrennt von der Anwendung zuweisen, also "IORef" Ich dachte, es würde schön kommen.

Objekte in objektorientierten Sprachen sind nicht nur IORef-ähnlich (gemeinsam genutzt und veränderbar), sondern auch nicht gemeinsam genutzte Objekte, unveränderliche Objekte, unveränderliche und seitenlose Objekte (z. B. keine E / A-Eingabe / Ausgabe) und Nebenwirkungen. Ich denke, es gibt verschiedene Dinge wie große Objekte (Objekte, die die Verarbeitung umschließen, die mit dem externen Server interagiert), aber ich konnte nicht gut darüber nachdenken, deshalb werde ich dieses Beispiel hier nur vorstellen. Ich werde es behalten.

Impressionen

Standard Pascal hat nur wenige Funktionen und Sie müssen "toInt" oder "toStr" selbst implementieren oder den Speicher selbst freigeben, aber dieser (umständliche) Vorgang ist abgeschlossen. Wenn ich es schreibe, fühle ich mich gerne wie "Oh, ich programmiere." Ich habe das Gefühl, ich spiele mit einem manuellen Auto anstelle eines automatischen Autos. In diesen Tagen ist Go so.

Obwohl Java auf verschiedene Weise kritisiert wurde, denke ich, dass es eine gute Sprache ist, die einfach zu bedienen ist. Ich bin der Meinung, dass die objektorientierte Programmiersprache besser für Metaprogrammierung wie Ruby und das dynamische Erstellen von Objekten wie JavaScript geeignet ist, aber es besteht ein Sicherheitsgefühl, dass die Klasse statisch bestimmt wird. Hört sich gut an. Ich mag Java 1.5 oder so und es ist eine Schande, dass es mit so vielen anderen Paradigmen kompliziert wird.

Haskell hat viel zu lernen und kämpft, aber ich bin sehr glücklich, wenn es funktioniert. Es gibt viele Funktionen und es ist schwer zu verstehen und sich zu erinnern, aber es scheint eine große Sache zu sein, wenn man weiß, wie man es benutzt, und es ist schön, eine statische Lösung zu haben. Ich denke, es ist gut, vorsichtig mit dem Typ zu sein.

abschließend

In diesem einfachen Beispiel ändert sich an keiner Implementierung viel, aber ich habe mich gefragt, ob es noch einige Funktionen für jede Sprache gibt.

Die Anzahl der Programmiersprachen ist heutzutage ein Multi-Paradigma, weshalb ich es für wichtig halte, die Grundlagen der Single-Paradigma-Sprachen zu lernen.

Wenn Sie Fehler oder Unzulänglichkeiten haben, kommentieren Sie bitte.

Recommended Posts

Versuchen Sie, ein Zusatzprogramm in mehreren Sprachen zu erstellen
Versuchen Sie, n-ary Addition in Java zu implementieren
Ich habe versucht, eine Anwendung in 2 Sprachen zu entwickeln
Ich habe versucht, innerhalb von 3 Monaten einen Antrag von unerfahren zu stellen
[swift5] Versuchen Sie, einen API-Client auf verschiedene Arten zu erstellen
CompletableFuture Erste Schritte 2 (Versuchen Sie, CompletableFuture zu erstellen)
Machen Sie mit Swift einen FPS-Zähler
Versuchen Sie, einen einfachen Rückruf zu tätigen
Versuchen Sie, Yuma in Ruby zu implementieren
Versuchen Sie einen If-Ausdruck in Java
Versuchen Sie, Yuma in Java zu implementieren
CompletableFuture Erste Schritte 2 (Versuchen Sie, CompletableFuture zu erstellen)
Versuchen Sie, einen Iterator zu erstellen, der einen Blick darauf werfen kann
Schnittstelle Versuchen Sie, Java-Problem TypeScript 7-3 zu machen
Versuchen Sie, Project Euler in Java zu lösen
Einfach, Slack Bot mit Java zu machen
Ich möchte eine ios.android App machen
Versuchen Sie, mit Scala mithilfe der Standardbibliothek von Java Text zu einem Bild hinzuzufügen
NLP4J Versuchen Sie, Annotator von 100 Sprachverarbeitung mit NLP4J auf # 34 "A B" zu bringen
So erstellen Sie eine Beurteilungsmethode, um nach einem beliebigen Zeichen im Array zu suchen
Ich möchte eine E-Mail in Java senden.
Versuchen Sie, ein Bulletin Board in Java zu erstellen
Zweite Abkochung: Versuchen Sie einen If-Ausdruck in Java
Inkrementierungsverhalten Versuchen Sie, Java zum Problem TypeScript 3-4 zu machen
Ich wollte (a == 1 && a == 2 && a == 3) in Java wahr machen
Zeichenfolgenoperation Versuchen Sie, das Java-Problem in TypeScript 9-3 zu ändern
So lösen Sie Ausdrucksprobleme in Java
So erstellen Sie ein ausführbares JAR in Maven
Wie man einen imposanten Android-Musikplayer macht