patternjavaMinor
Converting Morse Code
Viewed 0 times
codeconvertingmorse
Problem
Challenge
Write a program which reads a file containing Morse Code and outputs the conversion.
Specifications
Constraints
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
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:
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:
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
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
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:
and then:
You can then append that
I would probably take that inner stream and make a real
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
Additionally, the reading of the file, and converting to
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("-..-",
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.