Thursday, February 18, 2016

Concatenation of two or more strings in Bash

String concatenation is one of the basic requirements expected from any language. Bash provides various ways to do it. Let us see some of them here.

The most intuitive way to do string concatenation is by writing the strings next to each other.
For example, if the two strings are stored in a variable:
var1="Hello"
var2="World"
var3=$var1$var2
echo $var3
This would print the value of $var3 as:
HelloWorld
Note the lack of space between the two strings that are concatenated.

If you want to print it with space in between, then it has to be changed to:
var1="Hello"
var2="World"
var3="$var1 $var2"
echo $var3
In this case, you need to enclose the RHS of the assignment to var3 in double quote or store it in another variable.

A slightly different way of doing it is by using double quotes around the space alone:
var1="Hello"
var2="World"
var3=$var1" "$var2
echo $var3
If you watch carefully, that shows some interesting behavior. You can concatenate a string stored in a variable and a string literal by just placing one next to the other. This is useful when we have a file name and we want to add something else to name say .bak to say that it is a backup file. Let us try that:
filename="list_of_files.txt"
bak_filename=$filename".bak"
echo $bak_filename
This will give the output:
list_of_files.txt.bak
In fact, if bak is stored in a variable, then the quotes around the dot(".") is also optional:
filename="list_of_files.txt"
bak_extn="bak"
bak_filename=$filename.$bak_extn
echo $bak_filename
This will also give the same output:
list_of_files.txt.bak
But it will not work in all the cases. For example:
filename="list_of_files.txt"
bak_extn="bak"
bak_filename=$filename_$bak_extn
echo $bak_filename
This will give the output:
bak
To explain, only the value stored in the second variable was printed. The first variable was ignored. Why?
The culprit is the underscore("_"). Underscore is allowed as a special character in a variable name. So, instead of interpreting $filename as the variable name, the shell will interpret $filename_ as the variable name.
One way to do this is as follows:
var1="file_list"
var2="bak"
var3=$var1"_"$var2
echo $var3
var4=$var1\_$var2
In this case, the backslash ("\") specifies that the underscore is not a part of the previous variable. Another way to do this is as follows:
var1="file_list"
var2="bak"
var3=${var1}"_"${var2}
echo $var3
var4=${var1}_${var2}
This gives the output:
file_list_bak
file_list_bak
Of course, as usual, the double quotes are optional. There are people who find the usage of double quotes in for concatenation disturbing. They can use the curly braces instead.

Finally, let us see a special case. If you want to append a string to a string that is already stored in a variable, then use this method:
var1="Hello "
var2="World"
var1+=$var2
echo $var1
var1="Hello "
var1+="World"
echo $var1
This gives the output:
Hello World
Hello World
The operator += appends the string to the RHS of the operator to the variable on the LHS. So, in this case the value of the variable on the LHS will change.

Sunday, February 14, 2016

SQL - Columns in two tables showing the same value when queried

Consider a database storing the details of all schools and students in a city and the information of which student is in which school.
One way to model this is using the following tables:
Schools:
SchoolID Name MaxIntake
1 Knowledge School 1200
2 Wisdom School 670
Students:
StudentID Name SchoolID
1 Ram 2
2 Peter 1
3 Zaki 2

As is evident from the names, SchoolID in the table Students is a foreign key referring to the primary key SchoolID in the Schools table.
Now, if you want the list of all the students and their schools, the query would be:
select studentid, name, name from schools, students where schoolid = studentid;
But this query will show the same name for both the student and the school. Why?

I will try to explain this without going into the specifics of implementation of the specific DB engine. The part of the query from schools, students where schoolid = studentid will create a temporary table, like this:

SchoolID Name MaxIntake StudentID Name SchoolID
2 Wisdom School 670 1 Ram 2
1 Knowledge School 1200 2 Peter 1
2 Wisdom School 670 3 Zaki 2

This table has two fields named as name and when you query the value of name, you get the value from one of them.
To fix this, you need to provide an alias for the table names in the query and use them:
select studentid, st.name, sc.name from schools sc, student st where st.schoolid = sc.schoolid;
Now the table that would be created would be like:
sc.SchoolID sc.Name sc.MaxIntake st.StudentID st.Name st.SchoolID
2 Wisdom School 670 1 Ram 2
1 Knowledge School 1200 2 Peter 1
2 Wisdom School 670 3 Zaki 2

Since there is no conflict in the field names, the output would be fine.

Saturday, November 14, 2015

Java - foreach Loop

Foreach loop is a feature that has been present in many languages like Perl, Ruby etc for some time. Java finally implemented it in Java 5. The official documents refer to this new loop as the "Enhanced for Loop", the "For-Each Loop" and the "foreach statement".

To ensure that existing code is not broken, it was decided that a new keyword will not be introduced. The changes were done in the syntax of the for keyword.

Syntax:
for (declaration : expression) {
    // Statements (generally using the variable declared above)
}

An example of usage is as follows:
public class NewFor1 {
 public static void main(String[] args) {
  int[] arr = {100, 200, 300, 400, 500};

  for (int i:arr) {
   System.out.println(i);
  }
 }
}

This code generates the following output:
$ javac NewFor1.java
$ java NewFor1
100
200
300
400
500

As you can see, the program NewFor.java is a toy program. Generally, this loop is used while iterating over a Collection.

A couple of caveats:
Caveat 1. The first part of the for loop has to be a declaration.
public class NewFor2 {
 public static void main(String[] args) {
  int i = 0;
  int[] arr = {100, 200, 300, 400, 500};

  for (i:arr) {
   System.out.println(i);
  }
 }
}

$ javac NewFor2.java 
NewFor2.java:6: error: not a statement
  for (i:arr) {
       ^
NewFor2.java:6: error: ';' expected
  for (i:arr) {
        ^
NewFor2.java:6: error: ';' expected
  for (i:arr) {
         ^
3 errors
$
This syntax makes sense because the primary purpose of this loop is to make it easy to iterate through a collection. In such a case, declaring the variable in this place makes the most sense.

Caveat 2. What does the normal for loop provide that the foreach loop does not provide?
Answer: The index. While for loop provides an index which can also be used in the statements enclosed within the loop, the index cannot be obtained directly in the foreach loop.

Make combinations of directories with one command

As you know, the command mkdir is used to create directories. However, this command has some very useful options. To create a directory called myblog, you should use the following command:
$ ls -d myblog
ls: myblog: No such file or directory
$ mkdir myblog
$ echo $?
0
Perhaps the most commonly used option is the -p option. This option has multiple uses. If a directory exists and you give a mkdir command, normally, it will throw an error as follows:
$ ls -d myblog
myblog
$ mkdir myblog
mkdir: cannot create directory `myblog': File exists
$ echo $?
1
Notice that the exit value of the command is a non-zero value.
However, if we use the -p option, then we can get the following result:
$ ls -d myblog
myblog
$ mkdir -p myblog
$ echo $?
0
As you can see, there is no error thrown in this case and the command exits with a 0 in this case. When you are writing a script and you want to ensure that a directory is present, then this command can be used to confirm the same.
This option is also used for creating a whole directory structure with one command. For example, to create a directory called myblog and a directory under it called bash and one more under it called basic_commands, you would need to do the following if you don't use the -p option:
$ mkdir myblog/ && mkdir myblog/bash && mkdir myblog/basic_commands
mkdir: cannot create directory `myblog/': File exists
$ mkdir myblog/bash && mkdir myblog/bash/basic_commands
$ echo $?
0
$
In other words, you need to know if any part of the directory structure is already present. If so, you need to avoid creating it again. Also, the creation of the subdirectories requires you to type in the full path till that point again and again. This is a hassle and is error prone too.
The -p option helps here also:
$ ls myblog
$ mkdir -p myblog/bash/basic_commands
$ echo $?
0
There is one additional trick that you may not find in your man pages. If you want to create the following directory structure:
$ ls myblog myscript
myblog:
bash  python

myscript:
bash  python
$
You would need to execute the following command:
$ mkdir -p myblog/bash myblog/python myscript/bash myscript/python
Of course, you need to be careful to avoid typos while doing this. A better way to do this is using the following command:
$ mkdir -p {myblog,myscript}/{bash,python}
A more verbose way of specifying -p is --parents:
$ mkdir --parents {myblog,myscript}/{bash,python}
Talking of verbose, to see, what is happening when you use any of the commands above, add the -v or --verbose option:
$ mkdir -v -p {myblog,myscript}/{bash,python}
mkdir: created directory `myblog'
mkdir: created directory `myblog/bash'
mkdir: created directory `myblog/python'
mkdir: created directory `myscript'
mkdir: created directory `myscript/bash'
mkdir: created directory `myscript/python'
Neat Trick!

Monday, March 30, 2015

100 Doors

Question

There are D doors in a long hallway. They are all closed. The first time you walk by each door, you open it. The second time around, you clos-e every second door (since they are all opened). On the third pass you stop at every third door and open it if it’s closed, close it if it’s open. On the fourth pass, you take action on every fourth door. You repeat this pattern for I passes. D and I may or may not be equal. At the end of I passes, what doors are opened and what doors are closed?

Solution:

As with my general approach to programming, I am taking an iterative approach to the solution. We take a trivial approach first, an optimal solution with I == D and finally, an optimal solution with I <> D.

Trivial Solution:

An array of boolean values is initialized with `false` (implies closed) representing the state of the doors. It involves two loops. The outer one is for each pass and the inner loop is for the doors. The value in the element is toggled based on the two iterations.

Complexity

Since we have two loops both parsing through the complete length of the array, this solution for the problem has a time complexity of O(n2). However, the memory complexity is O(n) only.

Optimal Solution with I == D:

We use a single loop and determine if a number is a square or not. If the number is a perfect square of some other number, then it will be open. All other doors will be closed.

Explanation:

Consider the doors numbered from 1 to 10:
  1. This door is toggled once only - when the first pass is done. Hence, the state will be `true` at the end.
  2. This door is toggled two times - once during the first pass. Hence, the state will be `false` at the end.
  3. This door is toggled two times - once during the first pass. Hence, the state will be `false` at the end.
  4. This door is toggled three times - once during the first, second and fourth passes. Hence, the state will be `true` at the end.
  5. This door is toggled two times - once during the first and fifth passes. Hence, the state will be `false` at the end.
  6. This door is toggled four times - once during the first, second, third and sixth passes. Hence, the state will be `false` at the end.
  7. This door is toggled two times - once during the first and seventh passes. Hence, the state will be `false` at the end.
  8. This door is toggled four times - once during the first, second, fourth and eighth passes. Hence, the state will be `false` at the end.
  9. This door is toggled three times - once during the first, third and ninth passes. Hence, the state will be `true` at the end.
  10. This door is toggled four times - once during the first, second, fifth and tenth passes. Hence, the state will be `false` at the end.
As you can see, except for perfect squares, the other numbers have an even number of factors. When there are even number of factors, the door gets closed again.

Complexity

The optimal solution has a time complexity of O(n) and memory complexity of O(n).

Optimal Solution with I <> D

One of my friends found a problem with this logic. The discussion is documented here. I will be fixing the bug and updating this post at the earliest.
Now, let us consider another layer of complication on top of this. What would happen if the number of iterations is not equal to the number of doors in the room?
First off, we only need to consider the case where I > D, since there is no door whose state will be toggled because of the iterations.
For other cases, the logic of squares being closed and others being open would not be completely true any more. The last toggle of the doors where D > I would never happen. Now, the influencers are whether the door number is a perfect square and whether current door number < I. The following table gives the truth table after considering this scenario:
Is (door + 1) a perfect square? Is (door + 1) less than or equal to Number of iterations Is Door Closed?
true true true
false true false
true false false
false false true
As can be seen, this follows XNOR logic.

Complexity

This solution has a time complexity of O(n) and memory capacity of O(n).

Code

The code reference is in this github repo.

Sunday, November 30, 2014

Find occurrences of a file in directories where another specific file is present.

There are situations where as a Linux user, you want to find if a file, say .gitignore is present in the current directory and its children. Here is how you do it:
$ find . -name .gitignore
Now let us extend this command to get the list of directories in which these instances of .gitignore are present:
$ find . -name .gitignore | xargs -n1 dirname
The command xargs creates command line inputs from standard input to the command. As is the norm with Linux, this can be the output of other commands. In this case, it takes the total output of find and gives them one by one (because of the -n1) to dirname. As you might know, dirname strips the last part of a input file path.

One more step to check if another specified file exists in the same directory:
$ find . -name .gitignore | xargs -n1 dirname | xargs -n1 -I {} ls {}/README.md 2>/dev/null
Here, we use the same xargs command. The option -I is used to provide a string that can be replaced by the output of the command after this. In this case, the command is ls. In other words, the name of the directory is replaced with the directory name followed by the name of the second file (README.md). The error redirection is to ensure that :No such file or directory error is not thrown by the shell.

Mediator Design Pattern

Simply put, the mediator pattern is used when complex calling relationship between classes exists and we want to avoid placing the code to invoke various classes based on various considerations in all the classes involved.

Purpose

According to the GoF, the mediator pattern
Allows loose coupling by encapsulating the way disparate sets of objects interact and communicate with each other. Allows for the actions of each object set to vary independently of one another.

When to use this pattern?

  1. The pattern is useful in any scenario where we want to centralize the complex relationships between multiple classes.
  2. This pattern is extensively used for GUI programming.

Terminology

Before we go further, it makes sense to understand the terminology.

Mediator

This refers to the class that acts as the intermediate to redirect calls from one class to another.

Colleague

These are the classes that want to communicate with each other or are the actual actors in the flow.

Example

It is easier to understand this with an example. Consider a scenario where the code needs to be go through a flow. The flow contains various colleagues called in different order based on the user's input:

User Input Sequence of call for Colleagues
1 C1 ⇒ C2 ⇒ C3
2 C2 ⇒ C4 ⇒ C3 ⇒ C6
3 C1 ⇒ C5 ⇒ C2
4 C1 ⇒ (if value < 0) { C8 ⇒ C2 } else { C6 ⇒ C3 ⇒ C7 } ⇒ C4

Pictorially

This can be represented pictorially as follows:

This would mean that the code in each of the colleagues would need to have the intelligence to call the others. This leads to a lot of duplication of code.

When a mediator is used, the code organization looks like this:

Code Snippet

The complete code is present at my github repo. The following sections show a relevant part of the code.

Class Diagram

IColleague

package com.karthicksmail.designpatterns.mediator;

/**
 * @author karthicksmail
 */
public interface IColleague {
 void execute();
}

ConcreteColleague1

package com.karthicksmail.designpatterns.mediator;

/**
 * @author karthicksmail
 *
 */
public class ConcreteColleague1 implements IColleague {
 /* (non-Javadoc)
  * @see com.karthicksmail.designpatterns.mediator.IColleague#execute()
  */
 public void execute() {
  System.out.println("Executing " + this.getClass().getSimpleName());
 }

}
You can create as many ConcreteColleague classes as required.

Mediator

package com.karthicksmail.designpatterns.mediator;

import java.util.ArrayList;

/**
 * @author karthicksmail
 *
 */
public class Mediator {
 private ArrayList colleagueList = new ArrayList();
 private boolean value = false;

 public boolean register(IColleague colleague) {
  System.out.println("Registering " + colleague.getClass().getSimpleName());
  return colleagueList.add(colleague);
 }

 public void setValue(boolean  value) {
  this.value = value;
 }

 public void invokeFlow(int flow) {
  switch (flow) {
  case 1: 
   colleagueList.get(0).execute();
   colleagueList.get(1).execute();
   colleagueList.get(2).execute();
   break;
  case 2:
   colleagueList.get(1).execute();
   colleagueList.get(3).execute();
   colleagueList.get(2).execute();
   colleagueList.get(5).execute();
   break;
  case 3:
   colleagueList.get(0).execute();
   colleagueList.get(4).execute();
   colleagueList.get(1).execute();
   break;
  case 4:
   colleagueList.get(0).execute();
   if (value) {
    colleagueList.get(7).execute();
    colleagueList.get(1).execute();
   } else {
    colleagueList.get(5).execute();
    colleagueList.get(2).execute();
    colleagueList.get(6).execute();
   }
   colleagueList.get(3).execute();
   break;
  default:
   System.out.println("ALERT!!! Wrong flow ID");
  }
 }
}

MediatorMain

package com.karthicksmail.designpatterns.mediator;

public class MediatorMain {
 public static void main(String[] args) {
  Mediator mediator = new Mediator();
  ConcreteColleague1 concreteColleague1 = new ConcreteColleague1();
  ConcreteColleague2 concreteColleague2 = new ConcreteColleague2();
  ConcreteColleague3 concreteColleague3 = new ConcreteColleague3();
  ConcreteColleague4 concreteColleague4 = new ConcreteColleague4();
  ConcreteColleague5 concreteColleague5 = new ConcreteColleague5();
  ConcreteColleague6 concreteColleague6 = new ConcreteColleague6();
  ConcreteColleague7 concreteColleague7 = new ConcreteColleague7();
  ConcreteColleague8 concreteColleague8 = new ConcreteColleague8();

  mediator.register(concreteColleague1);
  mediator.register(concreteColleague2);
  mediator.register(concreteColleague3);
  mediator.register(concreteColleague4);
  mediator.register(concreteColleague5);
  mediator.register(concreteColleague6);
  mediator.register(concreteColleague7);
  mediator.register(concreteColleague8);

  for (int flowId = 0; flowId < 6; flowId++) {
   System.out.println("Flow Number " + flowId);
   mediator.invokeFlow(flowId);
  }
  System.out.println("Flow Number 4");
  mediator.setValue(true);
  mediator.invokeFlow(4);
  
 }
}

Output

Registering ConcreteColleague1
Registering ConcreteColleague2
Registering ConcreteColleague3
Registering ConcreteColleague4
Registering ConcreteColleague5
Registering ConcreteColleague6
Registering ConcreteColleague7
Registering ConcreteColleague8
Flow Number 0
ALERT!!! Wrong flow ID
Flow Number 1
Executing ConcreteColleague1
Executing ConcreteColleague2
Executing ConcreteColleague3
Flow Number 2
Executing ConcreteColleague2
Executing ConcreteColleague4
Executing ConcreteColleague3
Executing ConcreteColleague6
Flow Number 3
Executing ConcreteColleague1
Executing ConcreteColleague5
Executing ConcreteColleague2
Flow Number 4
Executing ConcreteColleague1
Executing ConcreteColleague6
Executing ConcreteColleague3
Executing ConcreteColleague7
Executing ConcreteColleague4
Flow Number 5
ALERT!!! Wrong flow ID
Flow Number 4
Executing ConcreteColleague1
Executing ConcreteColleague8
Executing ConcreteColleague2
Executing ConcreteColleague4

Advantages

  1. Increases reusability by ensuring that each of the colleague classes adhere to some simple requirements only.
  2. Since all complicated code is maintained at a single class, the surface area of risk is reduced.

Disadvantages

  1. The Mediator class can become very complex. This means that any change between any unrelated use case would require all use cases to be tested. It can lead to spaghetti code in the Mediator class. But this can be handled by following clean coding practices.

Wednesday, September 04, 2013

Bash & Sed - Display Unix Directory Structure as a Tree

Recently, I wanted to display a directory structure as a tree. However, I did not want to do programming in a high level language like Java or Ruby and parse the file structure etc. I wanted to use something like Bash.

So, here it goes:
find . -name '*' | sed -e 's/^/|-/' -e 's/[^-][^\/]*\//|   /g' -e 's/|   \([A-Za-z0-9_.]\)/|   +--\1/'

The output of this command will be like this:
|-.
|   +--a.out
|   +--arraydecl.c
|   +--coverage
|   |   +--a.out
|   |   +--cov.c
|   +--interposn
|   |   +--cmain.c
|   |   +--cparts.c
|   |   +--cparts.o
|   |   +--libwithso.so
|   |   +--main
|   |   +--main.c
|   |   +--main.o
|   |   +--withso.c
|   +--IPC
|   |   +--pipe.c
|   |   +--pipe_impl.c
|   +--KandR
|   |   +--detab.c
|   |   +--entab.c
|   |   +--Plan.txt
|   |   +--Plan.txt1
|   +--notbitand.c
|   +--ptrjoin.c
|   +--telewords.c

Now to explain this:
First use the find command:
find . -name '*'
This displays the name of all the files and directories under the current directory. Remember that using find would display the relative path from the current directory and does not follow symbolic links, by default.

Next, we need to split the sed command to understand it better.
-e 's/^/|-/'
This replaces the beginning of every line with the characters |-.

Next we have a more complex looking sed expression.
-e 's/[^-][^\/]*\//|   /g'
This replaces any element in the path to the file except the basename with three spaces.

Finally, the expression
-e 's/|   \([A-Za-z0-9_.]\)/|   +--\1/
This replaces the two spaces followed by an alphabet/number/underscore/dot with a "+--" followed by the same alphabet/number/underscore/dot. This gives the "+--" in the structure.

Sunday, August 04, 2013

Sha-bang

Scripts are text files which need to be executed as if they are executables. One way to do this is to execute the script by giving the name of the interpreter and the name of the script like this:
$ inter path/to/file
Here, inter should be replaced by the name of the interpreter. These would be the corresponding executables for the languages like ruby, perl, bash etc. But what if you don't want to expose which language the script is in?
The first thing that you need to do is provide executable permissions on the script. Once this is done, the script becomes eligible to be executed. In other words, it can be executed as follows:

$ path/to/file

A simplistic explanation of what happens is as follows1:
  1. The current process is forked and a copy of itself is created.
  2. The corresponding interpreter is exec'ed to execute the script with the right interpreter.

But how will the OS1 know the correct interpreter to use?

Enter Sha-bang!

Sha-bang is the term for the symbol #!. This has to be given as the first two characters in the script and should be followed by the path to the interpreter in the same line. Some examples are as follows:

#!/bin/bash

#!/usr/bin/ruby

The OS1 takes whatever is given after #! till the end of the line and does the exec using that.

It is important to remember that the rest of the line is totally taken and used. This means that you can have spaces before pass arguments to the interpreter. For example, -d can be passed to perl like this:

#!/usr/bin/perl -d

This would interpret the script as a Perl script and execute it in debug mode.

There is one catch in this method. What if the interpreter is installed in a different location from what is specified in the sha-bang?

The env command is used for this purpose. Here is the usage:

#!/usr/bin/env perl -d

This command finds the interpreter found in the environment variable $PATH and executes the command with the interpreter found.

1Please note that I am not trying to be technically correct. I am trying to explain things simply. For a proper understanding of this, I would suggest a book like Richard Stevens' Unix Network Programming.

Saturday, August 03, 2013

Ruby - Find Phone Numbers corresponding to Words

Remember the 1800 numbers a part of which match up with a name like "1-800 walmart".

I wanted to write a program to convert a name into the corresponding number. I decided to try this first with Ruby. So, here is version 1:

#!/usr/bin/env ruby

print "Enter the name: "
name = gets.chomp
downname=name.downcase

number = downname.gsub(/[abc]/,"2").gsub(/[def]/,"3")
                 .gsub(/[ghi]/,"4").gsub(/[jkl]/,"5")
                 .gsub(/[mno]/,"6").gsub(/[pqrs]/,"7")
                 .gsub(/[tuv]/,"8").gsub(/[wxyz]/,"9")
                 .gsub(/ /,"0") 

puts "The Number corresponding #{name} is #{number}"

The output for this is as follows:
$ ./name_2_number.rb 
Enter the name: Karthick
The Number corresponding Karthick is 52784425
$ ./name_2_number.rb 
Enter the name: 1800-walmart
The Number corresponding 1800-walmart is 1800-9256278

To explain the program:
  1. Line number 1 is the sha-bang. I will put in a separate post to explain that one.
  2. Line number 3 prints the message "Enter the name: ".
  3. Line number 4 accepts the input using gets and removes the \n at the end of the accepted by using chomp. Though not essential in this case, it is generally a good practice to do a chomp of the inputs obtained from the user.
  4. Line number 5 converts the name into lower case character. For this purpose, I am using the downcase method of the String in ruby. Changing the text into lower case helps in simplifying the regular expression (regex, for short) in the next line.
  5. Line number 7 is the one that contains the core logic. In this line, I use the gsub method in String class to replace occurrences of each of the letters with the corresponding numbers. Note that the first argument of gsub is a regex, while the second argument is a string. The sequence of gsub calls replace all occurrences of alphabets and spaces with the corresponding numbers.
  6. Line number 13 prints the message containing the original name given and the corresponding number. I have used puts over here because I want a newline to be appended to the end of the message. This is the difference between a print and puts in ruby. Also, I had created the variable downname so that I can use name in this display.
As you can see, this program works fine but there are some basic issues with this script:

  1. Line number 7 is not efficient. Multiple calls to gsub is the culprit. 
  2. Line number 7 is long and unwieldy.
  3. The maintainer of this code must understand the regex. Based on what I have seen, a surprising number of software engineers are not good with regex.

So, here is version 2:
#!/usr/bin/env ruby

print "Enter the name: "
name = gets.chomp
downname=name.downcase

repl = {'a' => '2', 'b' => '2', 'c' => '2',
        'd' => '3', 'e' => '3', 'f' => '3',
        'g' => '4', 'h' => '4', 'i' => '4',
        'j' => '5', 'k' => '5', 'l' => '5',
        'm' => '6', 'n' => '6', 'o' => '6',
        'p' => '7', 'q' => '7', 'r' => '7', 's' => '7',
        't' => '8', 'u' => '8', 'v' => '8',
        'w' => '9', 'x' => '9', 'y' => '9', 'z' => '9',
        ' ' => '0'}

number = downname.gsub(/[a-z ]/) { |m| repl[m] }

puts "The Number corresponding #{name} is #{number}"

Two lines have changed from the original script. Let me explain these two lines alone:
  1. Line number 7 declares a hash of the mapping between the each letter and its corresponding number. Note that this includes a blank space as one of the characters and it is mapped to 0.
  2. Line number 9 calls one gsub and does the replacement of the values by using the hash declared in the line number 7.
This version works fine in ruby 1.8 and 1.9. However, ruby 1.9 has a shortcut for line number 9 in version 2. Here is the modified script (version 3):
#!/usr/bin/env ruby

# Works with ruby version greater than 1.9

print "Enter the name: "
name = gets.chomp
downname=name.downcase

repl = {'a' => '2', 'b' => '2', 'c' => '2',
        'd' => '3', 'e' => '3', 'f' => '3',
        'g' => '4', 'h' => '4', 'i' => '4',
        'j' => '5', 'k' => '5', 'l' => '5',
        'm' => '6', 'n' => '6', 'o' => '6',
        'p' => '7', 'q' => '7', 'r' => '7', 's' => '7',
        't' => '8', 'u' => '8', 'v' => '8',
        'w' => '9', 'x' => '9', 'y' => '9', 'z' => '9',
        ' ' => '0'}

number = downname.gsub(/[a-z]/, repl)

puts "The Number corresponding #{name} is #{number}"
The new version of gsub does the replacement. It does this by taking the regular expression as the first argument and the second argument as the hash.

Cheers!
Karthick S.