HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Converting Morse Code

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
codeconvertingmorse

Problem

Challenge

Write a program which reads a file containing Morse Code and outputs the conversion.

Specifications

  • The first argument is a path to a file.



  • The file contains multiple lines.



  • Each line is a string of morse code.



  • Each letter is separated by a space.



  • Each word is separated by two spaces.



Constraints

  • The input file is correctly formatted.



  • The Morse Code strings are alphanumeric.



Sample Input

.- ...- ..--- .-- .... .. . -.-. -..-  ....- .....  
-... .... ...--


Sample Output


AV2WHIECX 45

BH3

Source

My Solution:

```
import java.io.File;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Solution {
private static final Map morseAlphabet = new HashMap<>();

static {
morseAlphabet.put(".-", 'A');
morseAlphabet.put("-...", 'B');
morseAlphabet.put("-.-.", 'C');
morseAlphabet.put("-..", 'D');
morseAlphabet.put(".", 'E');
morseAlphabet.put("..-.", 'F');
morseAlphabet.put("--.", 'G');
morseAlphabet.put("....", 'H');
morseAlphabet.put("..", 'I');
morseAlphabet.put(".---", 'J');
morseAlphabet.put("-.-", 'K');
morseAlphabet.put(".-..", 'L');
morseAlphabet.put("--", 'M');
morseAlphabet.put("-.", 'N');
morseAlphabet.put("---", 'O');
morseAlphabet.put(".--.", 'P');
morseAlphabet.put("--.-", 'Q');
morseAlphabet.put(".-.", 'R');
morseAlphabet.put("...", 'S');
morseAlphabet.put("-", 'T');
morseAlphabet.put("..-", 'U');
morseAlphabet.put("...-", 'V');
morseAlphabet.put(".--", 'W');
morseAlphabet.put("-..-", 'X');
morseAlphabet.put("-.--", 'Y');
morseAlphabet.put("--..", 'Z');
morseAlphabet.put("-----", '0');
morseAlphabet.put(".----", '1');
morseAlphabet.put("..---", '2');
morseAlphabet.put("...--", '3');
mor

Solution

Debatable ... and a bug?

constructs like these are one of those things that personal-preference plays in to, but consider:

if (args.length != 1) {
        System.err.println(args.length > 1 ? "Excessive arguments, only the first will be considered" : "No arguments provided.");
        System.exit(1);
    }


Error handling is boiler-plate that can be made more readable by separating the error conditions out explicitly. In checking/refactoring this, I found an apparent bug, too (which was hard to spot because of the compound exit condition). The bug is that if the arg-count is more than 1, you print the message "Excessive arguments, only the first will be considered" but then terminate anyway..... so much for ignoring the subsequent args... I would prefer:

private static final void terminate(String message) {
    System.err.println(message);
    System.exit(1);
}

.....

    if (args.length == 0) {
        terminate("No arguments provided");
    }
    if (args.length > 1) {
        terminate("Too many arguments provided. Expect exactly 1");
    }


A distinct advantage I find when you separate out the error conditions, is that maintaining them is simpler, especially when using version-control because the diffs are simpler to understand.

Challenge

Programming challenges are often time-constrained. While for most purposes your code is fast enough, there's a simple technique that can significantly improve performance - do bulk operations (at the expense of using memory). Specifically, it's typically better to "accumulate" the output in a "StringBuilder" and then have a single System.out.println() at the end. The in-the-loop println you currently have will cause the system to "flush" each line, and often that amounts to significant portions of your program time (more than half?).

Similarly, reading in the entire input file in one "gulp" is often faster than a Scanner, or some other operation.

Obviously, "huge" inputs may be a problem, in theory, but I have never yet been found victim to conditions like that in challenges.

Gulping

I would consider using a gulp function to read the entire file - the Java 8 java.nio.files package can help here, and I would choose:

List data = Files.readAllLines(Paths.get(args[0]));


Splitting

A trick of loading an empty-string in to your decode-routine to output a space, will allow you to use a regex to split, and process each line, in a stream:

private static final Pattern charsplit = Pattern.compile(" ");


and then:

String out = charsplit.splitAsStream(line)
             .map(letter -> morseAlphabet.get(letter))
             .collect(Collectors.joining(""));


You can then append that out with a newline, to the output buffer... so that the whole thing ends up looking a little like (untested - see conclusion for working code):

String output = Files.readAllLines(Paths.get(args[0])).stream()
     .map(line -> charsplit.splitAsStream(line)
                           .map(letter -> morseAlphabet.get(letter)
                           .collect(Collectors.joining("")))
     .collect(Collectors.joining("\n");

System.out.println(output);


I would probably take that inner stream and make a real Function out of it. I would also make the printing a separate function ... it should not be part of the parsing/decoding.

Conclusion

I put together a working example program here: https://ideone.com/hCpUA1

Note that the version of the code I was happiest with separated out the various functions, and converted the Map to a Map. Note that the map also has the "Space" value in it.

Additionally, the reading of the file, and converting to List is expected to be outside the function (in the main method) which cannot be done in ideone.com.

Here's the code in full:

```
import java.util.*;
import java.lang.*;
import java.io.*;
import java.util.stream.*;
import java.util.regex.Pattern;

class Morse
{

private static final Map morseAlphabet = new HashMap<>();

static {
morseAlphabet.put("", " ");
morseAlphabet.put(".-", "A");
morseAlphabet.put("-...", "B");
morseAlphabet.put("-.-.", "C");
morseAlphabet.put("-..", "D");
morseAlphabet.put(".", "E");
morseAlphabet.put("..-.", "F");
morseAlphabet.put("--.", "G");
morseAlphabet.put("....", "H");
morseAlphabet.put("..", "I");
morseAlphabet.put(".---", "J");
morseAlphabet.put("-.-", "K");
morseAlphabet.put(".-..", "L");
morseAlphabet.put("--", "M");
morseAlphabet.put("-.", "N");
morseAlphabet.put("---", "O");
morseAlphabet.put(".--.", "P");
morseAlphabet.put("--.-", "Q");
morseAlphabet.put(".-.", "R");
morseAlphabet.put("...", "S");
morseAlphabet.put("-", "T");
morseAlphabet.put("..-", "U");
morseAlphabet.put("...-", "V");
morseAlphabet.put(".--", "W");
morseAlphabet.put("-..-",

Code Snippets

if (args.length != 1) {
        System.err.println(args.length > 1 ? "Excessive arguments, only the first will be considered" : "No arguments provided.");
        System.exit(1);
    }
private static final void terminate(String message) {
    System.err.println(message);
    System.exit(1);
}

.....

    if (args.length == 0) {
        terminate("No arguments provided");
    }
    if (args.length > 1) {
        terminate("Too many arguments provided. Expect exactly 1");
    }
List<String> data = Files.readAllLines(Paths.get(args[0]));
private static final Pattern charsplit = Pattern.compile(" ");
String out = charsplit.splitAsStream(line)
             .map(letter -> morseAlphabet.get(letter))
             .collect(Collectors.joining(""));

Context

StackExchange Code Review Q#135132, answer score: 6

Revisions (0)

No revisions yet.