4
\$\begingroup\$

I've written a little (imperative) java program that basically prints out all french written numbers from 1 to 1000. (I'm learning how to write them)

Based on the first numbers (1-16) and the tens and hundreds, It chains the words of the next number together and writes them into an one dimensional array.

The French counting system is a bit unusual as e.g. the numbers from 60 to 99 are based on twenty, instead of the normal, decimal 10. That means, the next number after 69 ("soixante-neuf" => 60+9) is written out 60+10 ("soixante-dix") instead of just 70.

In the program, I've used basically the modulo (e.g. 70 % 20 => 10) operator to detect and print the particular digits.

My Questions are

  1. how I could make the program generally "better" => what could i do to improve readability, the "length" of the code and make it in general more "smart"
  2. In particular how could I profit from object orientated programming in this particular example, as the normal OOP advantages like security, separated frontend/backend don't help me here very well. (at least in my opinion)

The Code:

public class Main {

    public static void main(String args[]) {

        // i want to print also 1000
        String[] french_num = new String[1001];

        french_num[0] = "zero";
        french_num[1] = "un";
        french_num[2] = "deux";
        french_num[3] = "trois";
        french_num[4] = "quatre";
        french_num[5] = "cinq";
        french_num[6] = "six";
        french_num[7] = "sept";
        french_num[8] = "huit";
        french_num[9] = "neuf";
        french_num[10] = "dix";
        french_num[11] = "onze";
        french_num[12] = "douze";
        french_num[13] = "treize";
        french_num[14] = "quatorze";
        french_num[15] = "quinze";
        french_num[16] = "seize";
        french_num[20] = "vingt";
        french_num[30] = "trente";
        french_num[40] = "quarante";
        french_num[50] = "cinquante";
        french_num[60] = "soixante";
        french_num[80] = "quatre-vingt";
        french_num[100] = "cent";
        french_num[200] = "deux-cent";
        french_num[300] = "trois-cent";
        french_num[400] = "quatre-cent";
        french_num[500] = "cinq-cent";
        french_num[600] = "six-cent";
        french_num[700] = "sept-cent";
        french_num[800] = "huit-cent";
        french_num[900] = "neuf-cent";
        french_num[1000] = "mille";

        int french_prefix;
        int french_suffix;

        // write only the numbers from 16 to 70 into the array
        // 16 - 70
        for (int i = 16; i < 70; i++) {

            // use existing values if possible
            if (french_num[i] == null) {

                /*
                in this context, french_suffix contains the last decimal digit, as 16-70 is base 10.
                in this context, french_prefix contains the first decimal digit
                for instance, the number 17:
                    => french_suffix == 7 ("sept")
                    => french_prefix == 10 ("dix")
                */
                french_suffix = i % 10;
                french_prefix = i - (i % 10);



                // 0 - 69
                if ((0 < i) && (i < 70)) {

                    if (i % 10 == 1) {
                        // 21, 31, etc. need to have "-et-un"
                        french_num[i] = french_num[french_prefix] + "-et-" + french_num[french_suffix];
                    } else {
                        french_num[i] = french_num[french_prefix] + "-" + french_num[french_suffix];
                    }
                }
            }
        }
        // write only the numbers from 69 to 99 into the array; as it can use the existing values of the
        // former calculations. => needs just to add the tens
        // 69 - 99
        for (int i = 69; i < 100; i++) {

            // use existing values if possible
            if (french_num[i] == null) {
                /*
                in this context, french_suffix contains the last vigesimal digit
                in this context, french_prefix contains the first vigesimal digit
                for instance, the number 74:
                    => french_suffix == 17 ("dix-sept")
                    => french_prefix == 60 ("soixante")
                */
                french_suffix = i % 20;
                french_prefix = i - (i % 20);

                french_num[i] = french_num[french_prefix] + "-" + french_num[french_suffix];
            }
        }

        // write only the numbers from 100 to 1000 into the array; as it can use the existing values of the
        // former calculations. => needs just to add the hundreds
        // 100 - 1000
        for (int i = 100; i < french_num.length; i++) {

            // use existing values if possible
            if (french_num[i] == null) {
                /*
                in this context, french_suffix contains the last vigesimal digit
                in this context, french_prefix contains the first vigesimal digit
                for instance, the number 343:
                    => french_suffix == 43 ("quarante-trois")
                    => french_prefix == 300 ("trois-cent")
                */
                french_suffix = i % 100;
                french_prefix = i - (i % 100);

                if (i % 100 == 1) {
                    // prints all numbers, which have a one as the trailing digit. (e.g. 361, 101, etc.)
                    french_num[i] = french_num[french_prefix] + "-et-" + french_num[french_suffix];
                } else {
                    // print  all other numbers, which don't need special treatment. (e.g. 355, 693)
                    french_num[i] = french_num[french_prefix] + "-" + french_num[french_suffix];
                }
            }
        }

        // print the result
        for (int i = 0; i < french_num.length; i++) {
            System.out.println(french_num[i] + "," + i);
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ Are you on Java 8? \$\endgroup\$ – h.j.k. Oct 18 '15 at 12:33
  • \$\begingroup\$ I'm currenctly using Java 7. What do you have in mind? \$\endgroup\$ – toogley Oct 18 '15 at 14:43
  • 1
    \$\begingroup\$ Similar to @janos's answer, you can use a Map<Integer, String> for the 'base' transformations, and run an IntStream (Java 8 specific) to map them to the desired results. Wells, since you're still on Java 7, @janos's answer will be a lot more suitable. :) \$\endgroup\$ – h.j.k. Oct 18 '15 at 15:51
5
\$\begingroup\$

how i could profit from object orientated programming in this particular example, as the normal oop advantages like security, separated frontend/backend don't help me here very well. (at least in my opinion)

Start by organizing it better. Currently you do everything in a single main method. This is not ideal. Ideally you should follow the single responsibility principle, and have program elements such as methods and classes and are in charge of doing one thing alone.

For example:

  • What is the purpose of the class? "Main" is not a purpose. Something like FrenchCounting would be better.

  • Instead of one main method, it will make sense to have a method called toFrench(int num), which takes a number and returns a String. That way the logic you implemented will become more usable from other classes.

  • The french_num array can be a (private) static field of the class. The toFrench method can reuse it, instead of recreating it every time.

  • Move printing to its own method

With the above applied, the class should end up with a structure more like this:

public class FrenchCounting {

    private static final Map<Integer, String> french;

    static {
        french = new HashMap<>();
        french.put(0, "zero");
        // ...
    }

    public static String toFrench(int num) {
        // ...
    }

    public static void main(String args[]) {
        for (int i = 0; i < 1000; ++i) {
            System.out.println(toFrench(i) + " , " + i);
        }
    }
}

This is more reusable, with a clear purpose, and the responsibilities better separated.

how I could make the program generally "better" => what could i do to improve readability, the "length" of the code and make it in general more "smart"

Filling an array of a 1000 strings, doesn't sound so good. You don't need to store all the calculated French numbers to print them. You could print them as you calculate, reducing your memory footprint.

It will be enough to store only the irregular numbers, and the ones that can be the building blocks to compose the others. There are many similar question on this site about printing numbers in English, you could learn much from those and adopt the logic similarly to the French language.

In Java the naming convention for variables is camelCase, not snake_case.

\$\endgroup\$
  • \$\begingroup\$ Do you have an suggestion on how i could store then the initial strings? (i mean, these basic ones i need to build difficult numbers, like 459) \$\endgroup\$ – toogley Oct 18 '15 at 14:54
  • 1
    \$\begingroup\$ A Map<Integer, String> seems to be an obvious choice \$\endgroup\$ – janos Oct 18 '15 at 15:18
  • \$\begingroup\$ Ah, thanks. I didn't knew that. But one of my biggest problems in this refactoring is how i could handle numbers e.g. 117,118 and 370-399 as i simply reused the former calculated ones (in this example 17,18 and 70-99). \$\endgroup\$ – toogley Oct 18 '15 at 15:32
  • \$\begingroup\$ Just like you did with an array: if the number was not calculated yet, then map.get(num) will return null \$\endgroup\$ – janos Oct 18 '15 at 15:34
  • 1
    \$\begingroup\$ You can store the irregular ones in the map. The others can be calculated as necessary. I amended my sample code at the top with an example of initializing the map, I hope it helps \$\endgroup\$ – janos Oct 18 '15 at 15:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.