Kevin's Blog

IT, Java, Web, Ubuntu

Category: Fundamental (page 1 of 3)

Look and Say Sequence (Java)

Look and say sequence is the sequence of numbers generated from the previous number by reading off the digits of which the number consists.

So the first number is 1 then there is one "1". one 1 -> 11
Now use the number "11" and there are two 1s. two 1s -> 21
21: one 2 and one 1 -> 1211
1211: one 1, one 2 and two 1s -> 111221
111221: three 1s, two 2s and one 1 -> 312211
312211: one 3, one 1, two 2s and two 1 -> 13112221
13112221: one 1, one 3, two 1s, three 2s and one 1 -> 1113213211
----------------------------------------------------------------
1, 11, 21, 1211, 111221, 312211, 13112221, 1113213211 ...

The Java programme code which reads off the given number can be written like

public static String LookAndSayUsingForLoop(final String number)
{
  if (null == number || number.isEmpty())
  {
    return "";
  }
  int firstCharPosition = 0;
  final StringBuilder stringBuilder = new StringBuilder();
  for (int i = 0; i < number.length(); i++)
  {
    if (number.charAt(firstCharPosition) != number.charAt(i))
    {
      //
      // the char at the current position is not equal to the first char
      // in the group of the equal chars so collect the chars
      // from the first char position to just before the current position.
      //
      final String digitsFound = number.substring(firstCharPosition, i);
      stringBuilder.append(digitsFound
                          .length())
                   .append(number
                          .charAt(firstCharPosition));
      firstCharPosition = i; // set the new first char position
    }
  }
  /* add the leftover */
  stringBuilder.append(number
                      .substring(firstCharPosition,
                                 number.length())
                      .length())
               .append(number
                      .charAt(firstCharPosition));
  return stringBuilder.toString();
}
System.out.println(LookAndSayUsingForLoop("1"));
Result:
11
System.out.println(LookAndSayUsingForLoop("11"));
Result:
21
System.out.println(LookAndSayUsingForLoop("21"));
Result:
1211

So to get the first ten elements,

String result1 = number;
System.out.print(result1 + " ");
for (int i = 1; i < howMany; i++)
{
  result1 = LookAndSayUsingForLoop(result1);
  System.out.print(result1 + " ");
}

It prints out.

1 11 21 1211 111221 312211 13112221 1113213211 31131211131221 13211311123113112211 

It can be simpler if recursion is used.
The LookAndSayUsingForLoop method can be re-written like

private static String lookAndSayV1(final String number, final int position)
{
  if (number.length() == position)
  {
    // reached the end which means every digit in the number is
    // equal to one another so just read off the entire number.
    return String.valueOf(number.length()) + number.charAt(0);
  }
  final char firstChar = number.charAt(0);
  return firstChar == number.charAt(position) ?
            // the current char equals to the first one so keep checking
            lookAndSayV1(number, position + 1) :
            // otherwise, read off until just before the current position
            // then check from the current position
            // calling this function itself again.
            String.valueOf(number
                          .substring(0, position)
                          .length()) +
              firstChar +
              lookAndSayV1(number
                          .substring(position), 0);
}

The result is exactly the same as the one using ‘for’ loop.

System.out.println(lookAndSayV1("1", 0));
Result:
11
System.out.println(lookAndSayV1("11", 0));
Result:
21
System.out.println(lookAndSayV1("21", 0));
Result:
1211

To get the first ten elements, another recursive method can be used.

public static String lookAndSay(final int howMany, final String number)
{
  return 0 >= howMany ? "" : number + " " + lookAndSay(howMany - 1, lookAndSayV1(number, 0));
}
final String number = "1";
final String result = lookAndSay(10, number);
System.out.println(result);

Result:

1 11 21 1211 111221 312211 13112221 1113213211 31131211131221 13211311123113112211 

The method lookAndSayV1() can be re-written using only one return statement and a few conditional operations.

private static String lookAndSayV2(final String number, final int position)
{
  return number.length() == position ?
            String.valueOf(number
                          .length()) +
              number.charAt(0) :
            (number.charAt(0) == number.charAt(position) ?
                lookAndSayV2(number, position + 1) :
                String.valueOf(number
                              .substring(0, position)
                              .length()) +
                  number.charAt(0) +
                  lookAndSayV2(number
                            .substring(position), 0));
}

This works the same as the lookAndSayV1() method. The code would be more readable with readOff() method.

private static String readOff(final String digits)
{
  return String.valueOf(digits.length()) + digits.charAt(0);
}

private static String lookAndSayV3(final String number, final int position)
{
  return number.length() == position ?
            readOff(number) :
            (number.charAt(0) == number.charAt(position) ?
                lookAndSayV3(number, position + 1) :
                readOff(number
                      .substring(0, position)) +
                      lookAndSayV3(number
                                  .substring(position), 0));
}

public static String lookAndSay3(final int howMany, final String number)
{
  return 0 >= howMany ? "" : number + " " + lookAndSay3(howMany - 1, lookAndSayV3(number, 0));
}

If the numbers should be stored in a List object, it can be done like

public static void lookAndSayUsingList(final String number, final int howMany, final List<String> resultList)
{
  resultList.add(number);
  if (resultList.size() < howMany)
  {
    lookAndSayUsingList(lookAndSayV3(number, 0), howMany, resultList);
  }
}
final List<String> resultList = new ArrayList<String>();
lookAndSayUsingList(number, howMany, resultList);
System.out.println(resultList);

If you prefer having returned result,

private static List<String> lookAndSayUsingList2(final String number, final int howMany, final List<String> list)
{
  list.add(number);
  if (list.size() < howMany)
  {
    return lookAndSayUsingList2(lookAndSayV3(number, 0), howMany, list);
  }
  return Collections.unmodifiableList(new ArrayList<String>(list));
}

The last line which returns an unmodifiable list object should use a new ArrayList object instead of directly using list.
Just making list unmodifiableList is not enough as the caller of this method can still modify the given parameter list
e.g.) Just have Collections.unmodifiableList(list); will result in

final List<String> resultList = new ArrayList<String>();
resultList.add(number);
final List<String> result = lookAndSayUsingList2(resultList, howMany);
// result.add("test"); // <- this does not work! runtime exception!
resultList.add("test"); // <- yet this does!!!
// Now the result has "test" at the end although it's unmodifiable List.
// It was modified using resultList.

If you don’t want to pass a List object, create another method and use it with the lookAndSayUsingList2() method.

public static List<String> lookAndSayUsingList2(final String number, final int howMany)
{
  final List<String> list = new ArrayList<String>();
  return lookAndSayUsingList2(number, howMany, list);
}
System.out.println(lookAndSayUsingList2(number, howMany));

Finally, using a Function object.

public interface LookAndSayFunction
{
  void apply(String number, int howMany);
}
final List<String> resultListFromFunctionObject = new ArrayList<String>();
new LookAndSayFunction() {
  @Override
  public void apply(final String number, final int howMany)
  {
    resultListFromFunctionObject.add(number);
    if (resultListFromFunctionObject.size() < howMany)
    {
      apply(lookAndSayV3(number, 0), howMany);
    }
  }
}.apply(number, howMany);

System.out.println(resultListFromFunctionObject);

Don’t want to get a mutable List object staying outside the function?

public interface Function1<T, R>
{
  R apply(T input);
}

public interface Function2<T1, T2, R>
{
  R apply(T1 input1, T2 input2);
}
final Function2<String, Integer, List<String>> anotherFunction =
  new Function2<String, Integer, List<String>>() {
    @Override
    public List<String> apply(final String number, final Integer howMany)
    {
      return new Function<List<String>>() {
        @Override
        public List<String> apply()
        {
          final List<String> resultList = new ArrayList<String>();
          return new Function1<String, List<String>>() {
            @Override
            public List<String> apply(final String number)
            {
              resultList.add(number);
              if (resultList.size() < howMany.intValue())
              {
                return apply(lookAndSayV3(number, 0));
              }
              return Collections.unmodifiableList(resultList);
            }
          }.apply(number);
        }
      }.apply();
    }
  };

System.out.println(anotherFunction.apply(number, howMany));

Or currying can be used.

final Function1<Integer, Function1<String, List<String>>> lookAndSayWithCurrying =
new Function1<Integer, Function1<String, List<String>>>() {
  @Override
  public Function1<String, List<String>> apply(final Integer howMany)
  {
    return new Function1<String, List<String>>() {
      @Override
      public List<String> apply(final String number)
      {
        final List<String> resultList = new ArrayList<String>();
        return new Function1<String, List<String>>() {
          @Override
          public List<String> apply(final String number)
          {
            resultList.add(number);
            if (resultList.size() < howMany.intValue())
            {
              return apply(lookAndSayV3(number, 0));
            }
            return Collections.unmodifiableList(resultList);
          }
        }.apply(number);
      }
    };
  }
};
System.out.println(lookAndSayWithCurrying.apply(howMany)
        .apply(number));

The function can be reused.

final Function1<String, List<String>> lookAndSayToGet10Elements = lookAndSayWithCurrying.apply(10);
System.out.println(lookAndSayToGet10Elements.apply(number));
System.out.println(lookAndSayToGet10Elements.apply(number));
Result:
[1, 11, 21, 1211, 111221, 312211, 13112221, 1113213211, 31131211131221, 13211311123113112211]
[1, 11, 21, 1211, 111221, 312211, 13112221, 1113213211, 31131211131221, 13211311123113112211]
final Function1<String, List<String>> lookAndSayToGet5Elements = lookAndSayWithCurrying.apply(5);
System.out.println(lookAndSayToGet5Elements.apply(number));
Result:
[1, 11, 21, 1211, 111221]
System.out.println(lookAndSayToGet5Elements.apply("111221"));
Result:
[111221, 312211, 13112221, 1113213211, 31131211131221]
System.out.println(lookAndSayToGet5Elements.apply("21"));
Result:
[21, 1211, 111221, 312211, 13112221]
System.out.println(lookAndSayToGet5Elements.apply("1"));
Result:
[1, 11, 21, 1211, 111221]

If you want to fix the number and get different numbers of elements as the results from the look and say function,

final Function1<String, Function1<Integer, List<String>>> lookAndSayWithCurryingAndNumberFixed =
  new Function1<String, Function1<Integer, List<String>>>() {
    @Override
    public Function1<Integer, List<String>> apply(final String number)
    {
      return new Function1<Integer, List<String>>() {
        @Override
        public List<String> apply(final Integer howMany)
        {
          final List<String> resultList = new ArrayList<String>();
          return new Function1<String, List<String>>() {
            @Override
            public List<String> apply(final String number)
            {
              resultList.add(number);
              if (resultList.size() < howMany.intValue())
              {
                return apply(lookAndSayV3(number, 0));
              }
              return Collections.unmodifiableList(resultList);
            }
          }.apply(number);
        }
      };
    }
  };
final Function1<Integer, List<String>> lookAndSayWithNumberFixed = lookAndSayWithCurryingAndNumberFixed.apply("1");
System.out.println(lookAndSayWithNumberFixed.apply(3));
System.out.println(lookAndSayWithNumberFixed.apply(5));
System.out.println(lookAndSayWithNumberFixed.apply(10));

Results:

[1, 11, 21]
[1, 11, 21, 1211, 111221]
[1, 11, 21, 1211, 111221, 312211, 13112221, 1113213211, 31131211131221, 13211311123113112211]

Here are all the examples.

Results:

Using LookAndReadUsingForLoop: 
String result1 = number;
System.out.print(result1 + " ");
for (int i = 1; i < howMany; i++)
{
  result1 = LookAndSayUsingForLoop(result1);
  System.out.print(result1 + " ");
}
1 11 21 1211 111221 312211 13112221 1113213211 31131211131221 13211311123113112211 
--------------------------------------------------------------------------------

Using lookAndSayV1: 
System.out.println(lookAndSay(howMany, number));
1 11 21 1211 111221 312211 13112221 1113213211 31131211131221 13211311123113112211 
--------------------------------------------------------------------------------

Using lookAndSaV2: 
System.out.println(lookAndSay2(howMany, number));
1 11 21 1211 111221 312211 13112221 1113213211 31131211131221 13211311123113112211 
--------------------------------------------------------------------------------

Using lookAndSaV3: 
System.out.println(lookAndSay3(howMany, number));
1 11 21 1211 111221 312211 13112221 1113213211 31131211131221 13211311123113112211 
--------------------------------------------------------------------------------

Using lookAndSayUsingList: 
final List<String> resultList = new ArrayList<String>();
lookAndSayUsingList(number, howMany, resultList);
System.out.println(resultList);
[1, 11, 21, 1211, 111221, 312211, 13112221, 1113213211, 31131211131221, 13211311123113112211]
--------------------------------------------------------------------------------

Using lookAndSayUsingList2: 
System.out.println(lookAndSayUsingList2(number, howMany, new ArrayList<String>()));
[1, 11, 21, 1211, 111221, 312211, 13112221, 1113213211, 31131211131221, 13211311123113112211]
--------------------------------------------------------------------------------

Using another lookAndSayUsingList2 (without passing List): 
System.out.println(lookAndSayUsingList2(number, howMany))
--------------------------------------------------------------------------------

Using Function object: 
final List<String> resultListFromFunctionObject = new ArrayList<String>();
new LookAndSayFunction() {
  @Override
  public void apply(final String number, final int howMany)
  {
    resultListFromFunctionObject.add(number);
    if (resultListFromFunctionObject.size() < howMany)
    {
      apply(lookAndSayV3(number, 0), howMany);
    }
  }
}.apply(number, howMany);
System.out.println(resultListFromFunctionObject);
[1, 11, 21, 1211, 111221, 312211, 13112221, 1113213211, 31131211131221, 13211311123113112211]
--------------------------------------------------------------------------------

Using Function object with returned List object: 
final Function2<String, Integer, List<String>> anotherFunction = new Function2<String, Integer, List<String>>() {
  @Override
  public List<String> apply(final String number, final Integer howMany)
  {
    return new Function<List<String>>() {
      @Override
      public List<String> apply()
      {
        final List<String> resultList = new ArrayList<String>();
        return new Function1<String, List<String>>() {
          @Override
          public List<String> apply(final String number)
          {
            resultList.add(number);
            if (resultList.size() < howMany.intValue())
            {
              return apply(lookAndSayV3(number, 0));
            }
            return Collections.unmodifiableList(resultList);
          }
        }.apply(number);
      }
    }.apply();
  }
};
System.out.println(anotherFunction.apply(number, howMany));
[1, 11, 21, 1211, 111221, 312211, 13112221, 1113213211, 31131211131221, 13211311123113112211]

System.out.println(anotherFunction.apply(number, 5));
[1, 11, 21, 1211, 111221]

System.out.println(anotherFunction.apply(number, 7));
[1, 11, 21, 1211, 111221, 312211, 13112221]
--------------------------------------------------------------------------------

Using currying (number of elements fixed): 
final Function1<Integer, Function1<String, List<String>>> lookAndSayWithCurrying =
  new Function1<Integer, Function1<String, List<String>>>() {
    @Override
    public Function1<String, List<String>> apply(final Integer howMany)
    {
      return new Function1<String, List<String>>() {
        @Override
        public List<String> apply(final String number)
        {
          final List<String> resultList = new ArrayList<String>();
          return new Function1<String, List<String>>() {
            @Override
            public List<String> apply(final String number)
            {
              resultList.add(number);
              if (resultList.size() < howMany.intValue())
              {
                return apply(lookAndSayV3(number, 0));
              }
              return Collections.unmodifiableList(resultList);
            }
          }.apply(number);
        }
      };
    }
  };
lookAndSayWithCurrying.apply(howMany)
    .apply(number);
[1, 11, 21, 1211, 111221, 312211, 13112221, 1113213211, 31131211131221, 13211311123113112211]

final Function1<String, List<String>> lookAndSayToGet10Elements = lookAndSayWithCurrying.apply(10);
System.out.println(lookAndSayToGet10Elements.apply(number));
[1, 11, 21, 1211, 111221, 312211, 13112221, 1113213211, 31131211131221, 13211311123113112211]

System.out.println(lookAndSayToGet10Elements.apply(number));
[1, 11, 21, 1211, 111221, 312211, 13112221, 1113213211, 31131211131221, 13211311123113112211]

System.out.println(lookAndSayToGet10Elements.apply(number));
[1, 11, 21, 1211, 111221, 312211, 13112221, 1113213211, 31131211131221, 13211311123113112211]


final Function1<String, List<String>> lookAndSayToGet5Elements = lookAndSayWithCurrying.apply(5);
System.out.println(lookAndSayToGet5Elements.apply(number));
[1, 11, 21, 1211, 111221]

System.out.println(lookAndSayToGet5Elements.apply(number));
[1, 11, 21, 1211, 111221]

System.out.println(lookAndSayToGet5Elements.apply(number));
[1, 11, 21, 1211, 111221]

System.out.println(lookAndSayToGet5Elements.apply("111221"));
[111221, 312211, 13112221, 1113213211, 31131211131221]

System.out.println(lookAndSayToGet5Elements.apply("21"));
[21, 1211, 111221, 312211, 13112221]

System.out.println(lookAndSayToGet5Elements.apply("1"));
[1, 11, 21, 1211, 111221]
--------------------------------------------------------------------------------

Using currying (number fixed): 
final Function1<String, Function1<Integer, List<String>>> lookAndSayWithCurryingAndNumberFixed =
  new Function1<String, Function1<Integer, List<String>>>() {
    @Override
    public Function1<Integer, List<String>> apply(final String number)
    {
      return new Function1<Integer, List<String>>() {
        @Override
        public List<String> apply(final Integer howMany)
        {
          final List<String> resultList = new ArrayList<String>();
          return new Function1<String, List<String>>() {
            @Override
            public List<String> apply(final String number)
            {
              resultList.add(number);
              if (resultList.size() < howMany.intValue())
              {
                return apply(lookAndSayV3(number, 0));
              }
              return Collections.unmodifiableList(resultList);
            }
          }.apply(number);
        }
      };
    }
  };
final Function1<Integer, List<String>> lookAndSayWithNumberFixed = lookAndSayWithCurryingAndNumberFixed.apply("1");
System.out.println(lookAndSayWithNumberFixed.apply(howMany));
[1, 11, 21, 1211, 111221, 312211, 13112221, 1113213211, 31131211131221, 13211311123113112211]

System.out.println(lookAndSayWithNumberFixed.apply(3));
[1, 11, 21]

System.out.println(lookAndSayWithNumberFixed.apply(5));
[1, 11, 21, 1211, 111221]

System.out.println(lookAndSayWithNumberFixed.apply(10));
[1, 11, 21, 1211, 111221, 312211, 13112221, 1113213211, 31131211131221, 13211311123113112211]

String Concatenation vs Conditional Operator in Java and JavaScript

Java:

System.out.println("client " + authorized ? "authorized" : "unauthorized");

JavaScript:

alert('client ' + authorized ? 'authorized' : 'unauthorized');

(Let’s assume that the authorized is a parameter variable)

In Java, if you write code like this, you definitely get a compile time error, since String concatenation gets higher precedence than conditional operator whereas in JavaScript, you don’t get any error although String concatenation has higher precedence just like Java.

Reason for that is unlike Java which allows only true or false (or boxed Boolean) for conditional expression, in JavaScript, there are several cases that make conditional expression working and make the evaluation result of it false (false, undefined, 0, Number.NaN, "" (empty String), etc.) or true (cases other than the ones making it false). So if you have some value other than those and test it, the result is true.

e.g.) some cases making the evaluation of conditional expression false.

if (0)
{
  // false so never come here.
}

if (undefined)
{
  // false so never come here.
}

if (Number.NaN)
{
  // false so never come here.
}

if (false)
{
  // false so never come here.
}

if ("")
{
  // false so never come here.
}

e.g.) some cases making the evaluation of conditional expression true.

if (1)
{
  // true!
}

if ("some value")
{
  // true!
}

if (true)
{
  // of course true!
}

if ({})
{
  // true!
}


if ({"someName":"someValue"})
{
  // true!
}


if ([])
{
  // true!
}


if ([1,2,3,])
{
  // true!
}

So this code

alert('client ' + authorized ? 'authorized' : 'unauthorized');

will eventually become like

if authorized is true,

alert('client true' ? 'authorized' : 'unauthorized');

then

alert('authorized');

if authorized is false,

alert('client false' ? 'authorized' : 'unauthorized');

then

alert('authorized');

if the caller of the function didn’t send the authorized.

alert('client undefined' ? 'authorized' : 'unauthorized');

then

alert('authorized');

So if you want to get the result you want, the conditional operator part should be wrapped in parentheses.

alert('client ' + (authorized ? 'authorized' : 'unauthorized'));

In Java, you must!

System.out.println("client " + (authorized ? "authorized" : "unauthorized"));

Do You Hate Java?

I quite agree with him.

What mistake most people make when they compare Java with other programming languages and talk about how bad Java is is that they only talk about the Java programming language but not Java platform. It is not really fair to talk about Java without thinking its ecosystem including the platform, frameworks, libraries, tools, etc. As Erik says in the talk above, if you use vim to programme Java code, it’s like pounding a nail with your head. People complain about static typing but with help from proper IDEs, it can actually increase programming productivity not to mention of easier debugging.

What about Java’s verbosity? Programming is a conversation between a computer and a human. Imagine, instead of using a human language, we use all mathematical symbols to talk, would it be so easy to understand?


What does this picture mean? It might be 2? or victory? But if I say, “two” or “victory” then it’s clear. There are places where simple symbols and icons are helpful (think about iPhone app icons and math problems) while there are some other places where verbose instructions are helpful. Verbosity in Java, in fact, helps you understand the code written by you or others. It doesn’t mean there is no room for Lambda (closure) in Java though. Java with Lambda can result in much simpler code, but without it, it’s still not that bad to read. Anyway, Lambda will come to Java soon (JDK 8 ), and I’m looking forward to it as it looks much nicer than its initial state. I do functional programming using Java in many places in my project so it would be nice to have Lambda, but without it, I’m still fine. What I more desire to have in Java is a way to retrieve the names of method parameters using reflection (or some other simple and easy way). I had to use ASM instead to solve this issue. Why do I need that? I needed it to develop JSON Statham, Java/Json mapper library, to use one of my projects that is mQlicker (audience response system). I can keep using ASM but it’s a library to decompose and modify Java bytecode so there can be some situations where I cannot use it (e.g. Android uses its own bytecode that is dalvik bytecode, and ASM doesn’t support it as far as I know).

Some people perhaps complain about the design patterns. If you don’t need it, you don’t have to or should not use it. We do not programme to apply the patterns. We do programme to solve our problems. We can use the patterns if these solve our problems. If applying it causes more problems, don’t use it. I don’t try to apply the patterns first. I code to solve a problem and later find my solution is actually some pattern A. It’s usually like this. When I know exactly what pattern solves my problem, then of course, I use it, but I’ve never coded in order to use the patterns I know unless I need to write some example code to show how to use the patterns.

Some others may say like “Framework A is too heavy and complex. It took me more time to learn than developing apps and still requires more time to develop than the time spent on development without it.” If it’s the case, you should not use it. You’ve got lots of other choices so why bother to stick to only one thing? Albert Einstein said that repeating the same thing over and over again and expecting different results is insane. You can try something else. Usually, if you feel like that about the framework you’re using, it’s either you don’t need it or you’re not good enough to use it. “you don’t need it” might mean the framework really sucks or it’s not appropriate to your project. “you’re not good enough to use it” probably means “you’re not good enough YET to use it and need more knowledge to learn it then will be able to use it properly.” Either way, try something else as well then you will possibly have some more idea of what to use. You don’t have to repeat the same thing and have the same bad result.

I know there are too many bad examples of Java code and projects which are enough to make a really bad impression of Java on people who try to learn and use it. Fortunately, owing to the Internet, we can easily find good ones too these days. If I read only those terrible Java code examples and books, I would probably not use Java. So don’t read too old Java books and go to some developer’s websites to find some good Java examples. I also have to admit that there are really horrible and poorly designed APIs in Java (e.g. JAXP DOM API, java.net.URL, java.util.Calendar, etc.). There are also no immutable collections in Java JDK (there are unmodifiable collections but these are not really immutable since the collection passed as the parameter of Collections.unmodifiableXxx() methods can be modified by the user of the Collections utility class or any objects having the reference to the parameter collection). These are bad, but it can be solved by using other nice libraries (e.g. XStream, XOM, dom4j, Joda-Time, DATE4J, Apache Commons, Guava, etc.).

Don’t be extremist. Object-oriented programming or functional programming is not the answer to your problems. You may need both to solve your problem or neither might help you solve your issue. It’s just one of the tools you can use to solve your problems. Don’t waste your time to argue which is superior to the other.

By the way, I don’t like Java. It’s just a tool. I can use whatever tools that solve or help me solve my problems. Wouldn’t you feel weird if I say “I like a hammer but hate a screwdriver”? I neither like nor dislike these tools. I just need a hammer to put a nail into a well and a screwdriver to tighten a screw. Wouldn’t it look so stupid if I attempt to pound a nail with a screwdriver or try to tighten a screw using a hammer? I use Java as it is just a proper tool to solve some of my problems. For other problems, I use some other tools.

Hmmm, come to think of it, I’d rather not waste my time on this kind of topic anymore.

Older posts

© 2014 Kevin's Blog

Theme by Anders NorenUp ↑

%d bloggers like this: