Summary: June Blender explains Doug Finke's multiplication and formatting trick. Today...the task.
Microsoft Scripting Guy, Ed Wilson, is here. Today, Honorary Scripting Guy, June Blender, examines a multiplication and formatting trick that Doug Finke posted on the PowerShell Facebook page. I've broken this article into two parts:
Part 1: The Task Explains what Doug was trying to do and how to do it in Windows PowerShell.
Part 2: The Method Explains the techniques that Doug used.
If you already understand The Task, you can wait for Part 2:The Method. Now, here's June...
Windows PowerShell MVP, Doug Finke, author of Windows PowerShell for Developers, is one of nicest and most clever people in the Windows PowerShell community. I'm a big fan, because I almost always learn something really new when I read Doug's work. You can contact Doug at:
- Website: http://dougfinke.com
- Twitter: @dfinke
Last week, Doug posted the following fun multiplication and formatting trick:
Many people "liked" the post (including me), but I realized that not everyone understood why it worked. If you do understand it, you might be able to use the techniques for slightly less fun things, like work. Let's tease it apart and learn all that Doug has to teach. This post explains what Doug was trying to do and how to do it in Windows PowerShell.
It looks like Doug had a bit of fun with the cool patterns of squares of numbers with 1s, and he wanted to create this table of products in Windows PowerShell.
1 x 1 = 1
11 x 1 = 11
11 x 11 = 121
111 x 111 = 12321
1111 x 1111 = 1234321
…
Each row of the table multiplies n digits of 1s and shows the product. The number of 1-digits increases with each row. To do this, he creates a loop that goes from 1 to 8 and prints that number of 1s and their product. It would look like:
- Row 1: Print one 1. Then print the product of 1 x 1.
- Row 2: Print two 1s. Then print the product of 11 x 11.
- …
- Row 8: Print eight 1s. Then print the product of 11111111 x 11111111.
To do this, you use a loop that starts at 1 and ends at your stopping point, which for Doug, is 8. This is a bit tricky, because you're working with strings, not numbers.
The essential part of this task is to print a specified number of 1s (twice). To do that, you use string multiplication, so let's start with that topic.
Multiplying strings
Most scripting and programming languages have operators that add and multiply numbers, for example:
PS C:\> 3 + 4
7 <yawn>
PS C:\> 3 * 4
12 <duh>
But Windows PowerShell lets you use the same + and * operators to add and multiply strings.
When you add strings, Windows PowerShell concatenates them (no spaces):
PS C:\> "Power" + "Shell"
PowerShell
When you multiply a string by a number n, Windows PowerShell copies the string n times and concatenates the results:
PS C:\> "Car" * 3
CarCarCar
String multiplication isn't commutative. Because the first operand determines the operation, the string must come first in a string multiplication expression. If an integer comes first, Windows PowerShell tries to do integer multiplication, which doesn't permit strings.
PS C:\> 3 * "Car"
Cannot convert value "Car" to type "System.Int32". Error: "Input string was not in a correct format."
At line:1 char:1
+ 3 * "Car"
+ ~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvalidCastFromStringToInteger
String multiplication lets you do some cool things. This For loops starts with $i = 1 and counts up to $i = 5. In each pass, it multiplies the "x" string by the value $i:
PS C:\> for ($i = 1; $i -le 5; $i++) {"x" * $i}
x
xx
xxx
xxxx
xxxxx
This is a handy trick. In languages that don't allow string multiplication, like Python and Java, you need nested loops to do this.
Doug has fun with the string "1". Note that "1" is a string enclosed in quotation marks, not an integer. In Doug's statement, he multiplies the "1" string by $i where i goes from 1 to 8:
for ($i = 1; $i -le 8; $i++)
{
"1" * $i
}
1
11
111
1111
11111
111111
1111111
11111111
This creates the first set of 1s for Doug's display. But he needs two sets of 1s on each row, separated by an " x ". We'll use string addition to get it.
Adding Strings
To get the first part of Doug's display, we can use a loop that multiplies a number by a "1" string . But Doug's table requires two sets of 1s, separated by an " x ".
To add the " x ", we'll use the addition operator (+). It concatenates the first set of ones, the " x " string, and the second set of ones.
("1" * $i) +" x " + ("1" * $i)
When $i is 1, it produces the first row. When $i is 2, it produces the second row:
PS C:\ps-test> $i = 1
PS C:\ps-test> ("1" * $i) + " x " + ("1" * $i)
1 x 1
PS C:\ps-test> $i = 2
PS C:\ps-test> ("1" * $i) + " x " + ("1" * $i)
11 x 11
Now, let's use a loop that starts $i at 1 and goes to 8 (inclusive):
for ($i = 1; $i -le 8; $i++)
{
"1" * $i + " x " + "1" * $i
}
1 x 1
11 x 11
111 x 111
1111 x 1111
11111 x 11111
111111 x 111111
1111111 x 1111111
11111111 x 11111111
Looking good!
The next part is" = " string, so let's add it:
for ($i = 1; $i -le 8; $i++)
{
"1" * $i + " x " + "1" * $i + " = "
}
1 x 1 =
11 x 11 =
111 x 111 =
1111 x 1111 =
11111 x 11111 =
111111 x 111111 =
1111111 x 1111111 =
11111111 x 11111111 =
The "1" * $i part of the equation is getting a bit repetitive (and error prone). Let's assign it to the $n variable. Then, we can replace the instances of "1" * $i with $n:
for ($i = 1; $i -le 8; $i++)
{
$n = "1" * $i #Assignment
$n + " x " + $n + " = "
}
1 x 1 =
11 x 11 =
111 x 111 =
1111 x 1111 =
11111 x 11111 =
111111 x 111111 =
1111111 x 1111111 =
11111111 x 11111111 =
Next, Doug wants to display the product of each equation. To get the product, he needs to multiply numbers, not strings. Let's see how he does it.
Invoke-Expression: Convert number strings to numbers
Doug wants to display the products of the numbers that he's printing:
11 * 1 = 11
11 * 11 = 121
111 * 111 = 12321
1111 * 1111 = 1234321
But, he was playing with strings of 1s, not the number 1. To convert the strings of numbers to numbers for the expression, he uses the Invoke-Expression cmdlet. Let's see how this works:
PS C:\> "2 * 3" #This is a string.
"2 * 3"
PS C:\> Invoke-Expression -Command "2 * 3" #This is a string.
PS C:\> 6
Now, let's try it with "1" strings:
PS C:\> "111 * 111" #This is a string.
"111 * 111"
PS C:\> Invoke-Expression -Command "111 * 111" #Still a string.
PS C:\> 12321
Be careful. To make this work, the value must be a single string, and all values and operators must be convertible to an arithmetic expression.
You can also pipe the number string to the Invoke-Expression cmdlet, which is what Doug did. And he used the iex alias of Invoke-Expression. The next three statements are equivalent, and they can be used interchangeably:
PS C:\> Invoke-Expression -Command "111 * 111"
PS C:\> 12321
PS C:\> "111 * 111" | Invoke-Expression
PS C:\> 12321
PS C:\> "111 * 111" | iex
PS C:\> 12321
This Invoke-Expression feature gives Doug the product for his "ones x ones = product" statement. Let's add the statement to the For loop block:
for ($i = 1; $i -le 8; $i++)
{
$n = "1" * $i
$n + " x " + $n + " = " +
("$n * $n" | iex)
}
1 x 1 = 1
11 x 11 = 121
111 x 111 = 12321
1111 x 1111 = 1234321
11111 x 11111 = 123454321
111111 x 111111 = 12345654321
1111111 x 1111111 = 1234567654321
11111111 x 11111111 = 123456787654321
This completes the task. It's tricky, but it works. In tomorrow's post, we'll talk about the techniques that Doug uses, including an interesting loop and pretty formatting. Stay tuned!
We invite you to follow us on Twitter and Facebook. If you have any questions, send email to scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow.
June Blender, Honorary Scripting Guy