[JAVA] Try to make an addition program in several languages

Introduction

At first, I summarized object-oriented programming and its features, but I couldn't do it well and gave up.

So, instead of that, I thought it would be better to create a simple task, program it concretely, and compare it, so I decided to start with an easy addition program.

As the language, I chose Pascal (standard Pascal) as the procedure-oriented language, Java as the object-oriented language, and Haskell as the functional language.

This language choice is completely a hobby. I can't say it well, but I like a language that has a single paradigm and static types, that tends to have a long syntax, and that feels a bit "squishy".

Addition does not just calculate in one line, but accepts input from the user, and defines the function as follows.

1.First, "Please enter a number. It ends with a blank line." Is displayed.
2. 「>Is displayed and waiting for input. If you enter a number and start a new line, it will be recorded.
3.again">Is displayed and waiting for input. Repeat until a line break, without entering any characters.
4.If a blank line break occurs, the result of adding the sum of all the numbers entered so far is displayed. "The total is xxx" is displayed.
5.Ignore inputs that include characters other than numbers.

Actually, I think that a slightly more complicated sample will bring out the individuality of each language, but the first step is to do so.

The actual operation image is like this.

Please enter the number. It ends with a blank line.
>10
>20
>4
>
The total is 34.

This article is intended for beginners who have learned one programming language and can understand conditional branching and loop processing.

Implementation in Pascal

Let's consider standard Pascal here. Standard Pascal has few features and can't be compiled separately, but I think it's still suitable for learning programming, especially the basics of learning algorithms.

First, consider implementing the main routine as follows:


program CalcSumApp;

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

//Definition of various functions

var
  lines : ItemPtr;
  sum : Integer;

begin
  writeln('Please enter the number. It ends with a blank line.');
  lines := readLines();
  sum := sumOfLines(lines);
  writeln('The total is' + toStr(sum) + 'is.');
  disposeAll(lines);
end.

First, we define a pointer called ItemPtr. Actually, in standard Pascal (ISO 7185 Pascal), variable length array cannot be used, so here we define a Node that can store only one numerical value called Item and input it in the form of a pointer. Changed to store the numerical value.

The main routine (the part below begin) looks like this:

The message is displayed on the first line, and the numerical value (multiple lines) is accepted on the second line. Then, in the 3rd line, the total of the entered numerical values is calculated, and in the 4th line, the result is displayed. toStr is a function that converts an integer to a string, but this is also not in standard pascal, so it is implemented in the program. The fifth line is freeing memory. You have to release the pointer yourself when you are done.

Now, let's consider the implementation of readLines in the input part.


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;

Enclose the whole in a while loop and make it a format that repeats until a condition (blank line feed input) is satisfied.

readln (line) is the part that receives the character input.

Only the first time is special and keeps the entered value in prevItemPtr. new (prevItemPtr) is an instruction to allocate an area in heap memory, stores the input numerical value there, and saves it in the result ( result). From the second time onward, the entered value is retained in currItemPtr. Then, the input numerical value is stored and referenced from the previous prevItemPtr. Finally, update currItemPtr as the new prevItemPtr.

Next is the sumOfLines function that calculates the addition.

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;

This converts the string to an integer and adds it, checking the lines line by line. The toInt function that converts a character string to an integer is also defined in the program because it does not exist in standard Pascal. In addition, when a character string other than a numerical value arrives, it is judged by the second argument err so as not to cause an error. (Therefore, this second argument is a reference call)

The whole program looks like this:

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('Please enter the number. It ends with a blank line.');
  lines := readLines();
  sum := sumOfLines(lines);
  writeln('The total is' + toStr(sum) + 'is.');
  disposeAll(lines);
end.

Implementation in Java

In Java, let's implement it with two classes, CalcSumApp class and CalcSum class. The first is the CalcSumApp class for the entire application.


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("Please enter the number. It ends with a blank line.");
    cs.readLines();
    int sum = cs.getSum();
    System.out.println("The total is" + String.valueOf(sum) + "is.");
  }
}

In the main function, create an instance of the CalcSumApp class and the calcSum class and call the start method.

The contents of the start method are almost the same as Pascal's main routine, but they do not deal directly with data, they just manipulate objects of the CalcSum class.

Then the CalcSum class looks like this:

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;
  }
}

The readLines method, which accepts input from the console, and the getSum method, which calculates the sum, are defined. The contents are almost the same as the readLines function and sumOfLines function in pascal. However, the input numerical data (list) is retained as an instance variable of the CalcSum class.

The whole program looks like this: Compared to Pascal, it is cleaner because there is no conversion process such as toInt or toStr, or process such as disposeAll of memory release, and it is nice that the list fits in CalcSum. (Although it may be better to make it private)

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("Please enter the number. It ends with a blank line.");
    cs.readLines();
    int sum = cs.getSum();
    System.out.println("The total is" + String.valueOf(sum) + "is.");
  }
}

Since it is easy in this example, I think that it is possible to implement CalcSum as an immutable object, but in general, in object-oriented programming, I think that the objects that can be immutable are limited, so I will not touch on the implementation that makes it immutable. did.

Implementation in Haskell

In Haskell, the main routine looks pretty much the same, and looks like this:

  main :: IO ()
  main = do
    hSetBuffering stdout NoBuffering
    putStrLn "Please enter the number. It ends with a blank line."
    lines <- readLines getLines
    list <- sequence lines
    let s = sum list
    putStrLn $ "The total is" ++ (show s) ++ "is."

The hSetBuffering stdout NoBuffering part, as pointed out in the comments in this article, was that without it, buffering does not guarantee that the inputs and outputs will be in the expected order. (Thanks to @igrep)

getLines is a list that receives an infinite number of strings and is defined as follows.


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

repeat creates an infinite list. In other words

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

It's like that. It is a feature of Hakell's delay processing that it can handle such an infinite list, and I wanted to use it like this.

readLines is a process to cut out the part until a blank line is input, and it is as follows.


  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

If the first element of the array is an empty string, that's it. Otherwise, Convert the first element to a number (readMaybe), and if it can be converted, add the converted one (recursively processed) and return it, and if it cannot be converted, convert the rest (recursively processed) Is returning only.

  list <- sequence lines 

It's a little confusing here, but I'm converting an array of IO to IO in an array and extracting its contents. So list is just an array of numbers.

Originally here

  list <- mapM (>>= return) lines

However, as pointed out in the comment, it can be solved in one shot with squence, so I changed it.

And the calculation of the total is done next.

   let s = sum list

(I will omit the story of strict evaluation because my understanding was wrong)

The whole program looks like this: The conversion of monads is a little troublesome, and unlike Java, the variable lines is handled directly, but by using the do syntax, it can be described like pascal.

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 "Please enter the number. It ends with a blank line."
    lines <- readLines getLines
    list <- sequence lines
    let s = sum list
    putStrLn $ "The total is" ++ (show s) ++ "is."

Bonus Haskell Part 2

Even in Haskell, I tried to create a data type and encapsulate it in an object-oriented manner. I don't think it's practical, but ...

The main routine is


  main :: IO ()
  main = do
    hSetBuffering stdout NoBuffering
    putStrLn "Please enter the number. It ends with a blank line."
    calcSum <- newCalcSum   -- calcSum = new CalcSum()
    inputStrings calcSum    -- calcSum.inputString()
    sum <- getSum calcSum   -- sum = calcSum.getSum()
    putStrLn $ "The total is" ++ (show sum) ++ "is."

It looks like this, and I think it's closer to Java. In the whole 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 "Please enter the number. It ends with a blank line."
    calcSum <- newCalcSum   -- calcSum = new CalcSum()
    inputStrings calcSum    -- calcSum.inputString()
    sum <- getSum calcSum   -- sum = calcSum.getSum()
    putStrLn $ "The total is" ++ (show sum) ++ "is."
  

What I wanted to say here is that the object (like thing) is ʻIORef. I think that objects have nothing to do with IO in the first place, but for me, objects are things that allocate an area in heap memory to read and write (an image like DB), so ʻIORef I thought it would come nicely.

Objects in object-oriented languages are IORef-like objects (shared and modifiable), non-shared objects, immutable objects, immutable objects with no side effects (such as no IO input / output), and side effects. I think there are various things such as large objects (objects that wrap the processing that interacts with the external server), but I could not think about them well, so I will just present this sample here. I will keep it.

Impressions

Standard Pascal has few features and you have to implement toInt or toStr yourself, or you have to free the memory yourself, but this (cumbersome) process is done. When I write it, I like to feel like "Oh, I'm programming." I feel like I'm playing with a manual car instead of an automatic car. These days, Go is like this.

Java has been criticized in many ways, but I think it's an easy-to-use and good language. I feel that object-oriented programming languages are better suited for metaprogramming like Ruby and dynamically creating objects like JavaScript, but there is a sense of security that classes are statically determined. Sounds good. I like Java 1.5 or so, and it's a shame that it gets complicated with other paradigms.

Haskell has a lot to study and is struggling, but I'm very happy when it works. There are a lot of functions and it's hard to understand and remember, but it feels like it's a big deal when you know how to use it, and it feels like it's solved statically. I think it's good to be careful about the type.

in conclusion

In this simple example, it doesn't change much in any implementation, but I wondered if there are still some features of each language.

The number of programming languages these days is multi-paradigm, and that's why I think it's important to learn the basics of single-paradigm languages.

If you have any mistakes or inadequacies, please comment.

Recommended Posts

Try to make an addition program in several languages
Try to implement n-ary addition in Java
I tried to develop an application in 2 languages
I tried to make an application in 3 months from inexperienced
[swift5] Try to make an API client with various methods
CompletableFuture Getting Started 2 (Try to make CompletableFuture)
Make an FPS counter in Swift
Try to make a simple callback
Try to implement Yubaba in Ruby
Try an If expression in Java
Try to implement Yubaba in Java
CompletableFuture Getting Started 2 (Try to make CompletableFuture)
Try to make a peepable iterator
Interface Try to make Java problem TypeScript 7-3
Try to solve Project Euler in Java
Easy to make Slack Bot in Java
I want to make an ios.android app
Try adding text to an image in Scala using the Java standard library
NLP4J [006-034b] Try to make an Annotator of 100 language processing knock # 34 "A's B" with NLP4J
I get an error when I try to use "^" or "$" in ruby ​​regular expression
How to make a judgment method to search for an arbitrary character in an array
I want to send an email in Java.
Try to create a bulletin board in Java
Second decoction: Try an If expression in Java
Increment behavior Try to make Java problem TypeScript 3-4
I wanted to make (a == 1 && a == 2 && a == 3) true in Java
String operation Try to make Java problem TypeScript 9-3
Try to make a music player using Basic Player
How to solve an Expression Problem in Java
How to make a follow function in Rails
How to build an executable jar in Maven
How to make an crazy Android music player