Twenty years ago, during my first year in academia, my Pascal tutor set us some top-notch assignments.
Your mission:
Write a program which draws a diamond of the form illustrated below. The letter which is to appear at the widest point of the figure (E in the example) is to be specified as input data.
Here’s a scan of the original hand-out:
My original Pascal solution, which I prototyped using BBC Basic V, took less than a page of fan-fold listing paper and was implemented as a console application. 50% of the listing dealt with input validation and “do you want to run the program again?” code! I will convert the example line-by-line into C# for later publication here! Of course, were I to write it today, it should look very different!
I’d be keen to see your solutions, written in your choice of programming language. Novelty value for uniqueness in your choice of programming language may well be rewarded! Procedural, object-oriented, functional, dynamic, verbose, terse…the choice is yours!
“What’s in it for me?” you might ask?
Well, nothing really, a bit of kudos and the feeling of a job well done! However, I will offer the two best/novel UK-based solutions a much-coveted DDD polo shirt (modelled here!). My decision is final, colour may vary, size might not be the same size as you, yada yada, other legalese applies, etc.
Submission by comments here on this post, by e-mail (top right About Me), or via Twitter @camurphy please!
Over to you!
UPDATE: Comments seem to mangle the code formatting, it has been suggested that code is submitted either via e-mail or via http://pastebin.com/
This is what I came up with… sure there must be a more elegant way!
static void Main(string[] args)
{
char c = Console.ReadKey().KeyChar;
Console.WriteLine();
const int numberOfRows = 5;
int rowNumber = 0;
List rows = new List();
int numberOfColumns = (numberOfRows * 2) – 1;
while (rowNumber Console.WriteLine(x));
rows.Reverse();
rows.RemoveAt(0); //we only want one “middle” row
rows.ForEach(x => Console.WriteLine(x));
}
Doh! Html cleaner mangled my code. Here tis:
http://pastebin.com/f46f98432
@mike – good solution; however I should perhaps have made it clear in the “spec” that the diamond must start at ‘A’ and roll on to the letter entered, i.e. it’s not limited to 5 rows!
Ahh I see! Well replacing the line
const int numberOfRows = 5;
with
int numberOfRows = (int)c – (int)’A’ + 1;
should sort that…
Something like this?
class Program
{
private const string alphabet = “abcdefghijklmnopqrstuvwxyz”;
static void Main(string[] args)
{
char c = Console.ReadKey().KeyChar;
var myChars = from character in alphabet
where character <= c
select character;
var fullRow = myChars.Skip(1).Reverse().Concat(myChars);
var myRows = (from character in myChars
select ConvertToSpaces(character, fullRow));
IEnumerable reverseRows = myRows.Reverse().Skip(1);
myRows = myRows.Concat(reverseRows);
Console.WriteLine(Environment.NewLine);//Skip the ReadKey line
foreach (string line in myRows)
Console.WriteLine(line);
Console.ReadKey();
}
private static string ConvertToSpaces(char wanted, IEnumerable characters)
{
return new string(
(from character in characters
select character == wanted ? character : ‘ ‘).ToArray());
}
}
Here is your T-SQL version, not a proper DB optimised version, most definitely a programmer wrangling T-SQL to his will. Works though.
—– —– —– —– —– —– —– —– —–
— Test on SQL Server 2000 using SQL 2005 Management Studio
DECLARE @OutmostChar AS CHAR(1)
SET @OutmostChar = ‘Z’
DECLARE @Result VARCHAR(2100)
SET @Result = ”
DECLARE @OutmostCharAscii INT
SET @OutmostCharAscii = ASCII(@OutmostChar)
IF (@OutmostCharAscii ASCII(‘Z’))
BEGIN
RAISERROR (N’Outmost character must be in range A-Z’, 10, 1);
END
DECLARE @NumberOfRowsToMiddle INT
SET @NumberOfRowsToMiddle = @OutmostCharAscii – ASCII(‘A’);
DECLARE @LoopVar INT
SET @LoopVar = 0
WHILE @LoopVar 0)
BEGIN
SET @Result = @Result + SPACE((@LoopVar * 2) – 1) +
CHAR(ASCII(‘A’) + @LoopVar)
END
SET @Result = @Result + CHAR(13) + CHAR(10)
SET @LoopVar = @LoopVar + 1
END
SET @LoopVar = @NumberOfRowsToMiddle
WHILE @LoopVar >= 0
BEGIN
SET @Result = @Result + SPACE(@NumberOfRowsToMiddle – @LoopVar) +
CHAR(ASCII(‘A’) + @LoopVar)
IF (@LoopVar > 0)
BEGIN
SET @Result = @Result + SPACE((@LoopVar * 2) – 1) +
CHAR(ASCII(‘A’) + @LoopVar)
END
SET @Result = @Result + CHAR(13) + CHAR(10)
SET @LoopVar = @LoopVar – 1
END
SELECT @Result
— In Results pane, right click single result and select “Save Results As ..” to save as CSV file to view in Notepad
And here is your T-SQL version with a link to the T-SQL code so it isn’t altered by Mr Murphy’s blogging engine.
Again, it’s not a proper DB optimised version, most definitely a programmer wrangling T-SQL to his will. Works though.
—– —– —– —– —– —– —– —– —–
http://www.tigernews.co.uk/blog-twickers/TSql-Diamond.txt
Since you requested it via twitter, here’s a LINQ sample. I cheated on the top and bottom lines with the ALine variable. Also, the For Each enumerations would be simplified in VB 10 using Statement Lambdas which are not available in VB 9 yet. I didn’t include validation on the input (but that would be trivial with a regular expression).
Sub Main()
Console.WriteLine(“Input the hightest letter to display. Leave blank to exit.”)
Dim HighestLetter = Console.ReadKey
If HighestLetter.Key = ConsoleKey.Enter Then Exit Sub
Dim ascLetter = Asc(HighestLetter.KeyChar)
Console.WriteLine()
Dim ALine = New String(” “c, ascLetter – 65) & “A”
Dim lines = (From c In Enumerable.Range(66, ascLetter – 65) _
Select line = New String(” “c, ascLetter – c) & Chr(c) & New String(” “c, (c – 66) * 2 + 1) & Chr(c), _
currentChar = c).ToArray
Console.WriteLine(ALine)
For Each line In lines
Console.WriteLine(line.line)
Next
For Each line In lines.OrderByDescending(Function(l) l.currentChar).Skip(1)
Console.WriteLine(line.line)
Next
Console.WriteLine(ALine)
Main() ‘ Loop for more tries
End Sub
I have the permission of one of the lads in work to post this.
Tom Vanhoutte he did it in about 20 minutes.
Dim res()
IDList=”ABCDEFGHIJKLMNOP”
Index1= Instr(IDList,”F”)
For i=1 to (Index1*2)-1
ReDim res(60)
res2=””
pleft = Abs(i-Index1)
Spaces=(Index1-(pleft+1))
pChar= mid(IDList,abs(Spaces)+1,1)
res(pleft)=pchar
res(pleft+(spaces*2))=pchar
for j=0 to 60
if IsEmpty(res(j)) then res(j)=” ”
res2=res2 & res(j)
next
rs=rs& res2 & vbcrlf
next
msgbox rs
Here’s mine – I cheated slightly because I left in the double AA at the top and the bottom but I prefer it that way, it feels more balanced! 🙂
ConsoleKeyInfo key = Console.ReadKey();
Console.Clear();
var query =
from i in Enumerable.Range(0, ((key.KeyChar – ‘A’) * 2) + 1)
let characterRange = key.KeyChar – ‘A’
let distanceFromMiddle = Math.Abs(characterRange – i)
let distanceFromEitherEnd = Math.Abs(characterRange – distanceFromMiddle)
let character = (char)(‘A’ + distanceFromEitherEnd)
select new string(‘ ‘, distanceFromMiddle) + character + new string(‘ ‘, distanceFromEitherEnd * 2) + character;
foreach (var item in query)
{
Console.WriteLine(item);
}
C’mon Mr Taulty there is no excuses for cheating – even slightly… This “bug” fix might help you!
ConsoleKeyInfo key = Console.ReadKey();
Console.Clear();
var query =
from i in Enumerable.Range(0, ((key.KeyChar – ‘A’) * 2) + 1)
let characterRange = key.KeyChar – ‘A’
let distanceFromMiddle = Math.Abs(characterRange – i)
let distanceFromEitherEnd = Math.Abs(characterRange – distanceFromMiddle)
let character = (char)(‘A’ + distanceFromEitherEnd)
select new string(‘ ‘, distanceFromMiddle) + character + (distanceFromEitherEnd == 0 ? String.Empty : new string(‘ ‘, (distanceFromEitherEnd * 2) – 1) + character);
foreach (var item in query)
{
Console.WriteLine(item);
}
This is F#. Well, one way of doing it in F# 🙂 No, I didn’t choose to do it that way because it’s the easiest to understand.
#light
open System
let endchar = Char.ToUpper (Console.ReadKey(true).KeyChar)
let rec lines c bs ms = seq {
let ss n = new String(‘ ‘, n)
let ts c = string(char(c))
let s = (ss bs) + (ts c) + (if ms = 0 then “” else (ss ms) + (ts c)) + (ss bs)
yield s
if bs > 0 then
yield! (lines (c + 1) (bs – 1)
(if ms = 0 then 1 else ms + 2))
yield s
}
Seq.iter (fun (l:string) -> Console.WriteLine(l))
(lines (int(‘A’)) (int(endchar) – int(‘A’)) 0)
Hm… in F#, indentation does in fact matter. Let me see if the comments can contain HTML here, maybe that will correct the formatting:
#light
open System
let endchar = Char.ToUpper (Console.ReadKey(true).KeyChar)
let rec lines c bs ms = seq {
let ss n = new String(‘ ‘, n)
let ts c = string(char(c))
let s = (ss bs) + (ts c) + (if ms = 0 then "" else (ss ms) + (ts c)) + (ss bs)
yield s
if bs > 0 then
yield! (lines (c + 1) (bs – 1)
(if ms = 0 then 1 else ms + 2))
yield s
}
Seq.iter (fun (l:string) -> Console.WriteLine(l))
(lines (int(‘A’)) (int(endchar) – int(‘A’)) 0)
[…] Programming Challenge! – Craig Murphy digs out a 20 year old programming problem from his past, and asks for solutions from his readers in a range of languages. A prize is being offered for the one Craig deems to be the best […]
Here’s a solution in Smalltalk…
[code]
| aChar startNum stopNum chars ctr totalChars |
“GS – Assume chars will be ASCII uppercase only”
startNum := 65.
“GS – Set the Character to print up to”
aChar := $E.
“GS – Set the stop number”
stopNum := aChar asUppercase asInteger.
“GS – Fill a collection with arrays representing the lines in half of the diamond”
totalChars := stopNum – startNum.
ctr := 0.
chars := OrderedCollection new.
startNum to: stopNum do: [ : i |
chars addLast:
(Array
“GS – Spaces from the left edge”
with: (totalChars – ctr)
“GS – The char to print”
with: (i asCharacter asUppercase)
“GS – The space between the two printed chars”
with: ((ctr * 2) -1)).
ctr := ctr + 1.
].
“GS – Clear the Transcript window”
Transcript clear.
“GS – Print the top half of the diamond”
chars do: [ : a |
(a at: 1)
timesRepeat:[Transcript show: Character space ].
Transcript show: (a at: 2).
((a at: 3) == -1)
ifTrue:[Transcript cr.]
ifFalse:[
(a at: 3)
timesRepeat:[Transcript show: Character space. ].
Transcript show: (a at: 2); cr.
].
].
“GS – Print the bottom half of the diamond”
ctr := 0.
chars reverseDo: [ : a |
(ctr == 0)
ifFalse:[
(a at: 1)
timesRepeat:[Transcript show: Character space ].
Transcript show: (a at: 2).
((a at: 3) == -1)
ifTrue:[Transcript cr.]
ifFalse:[
(a at: 3)
timesRepeat:[Transcript show: Character space. ].
Transcript show: (a at: 2); cr.
].
].
ctr := ctr + 1.
].
[/code]
OMG! That formatting is horrible!!!!
Solution in PERL: (not the most elegant way of doing it I’m sure, but it works!)
http://pastebin.com/f459f32ce
print “Input your character: ” . “\r\n”;
$c = getc(STDIN);
$NumberOfRows = ord($c) – ord(“A”);
$rowNumber = 0;
$NumberOfColumns = ($NumberOfRows*2) -1;
$rowNumber = 0;
$bool=0;
$multiple = 1;
while ($rowNumber <= $NumberOfRows*2)
{
if ($rowNumber == $NumberOfRows+1)
{
$bool = 1;
$multiple = -1;
}
$char = chr(ord($c) + ((-$NumberOfRows + $rowNumber) * $multiple));
print ” ” x (($NumberOfRows – $rowNumber)*$multiple) . $char;
if ($char gt “A”){print ” ” x ((($NumberOfRows*4*$bool) + ($rowNumber*2*$multiple))-1). $char;}
print “\r\n”;
$rowNumber++;
}
Bloody hell, that solution sucks, look at the size of it!!! Hmm, the Smalltalk is really rusty. I’ll be back with a better solution later 😉
Eep even some duplicate code in my example ($rowNumber is THAT important that I reset it twice…)
I tried a version in LOLCODE but the lack of multiplication and conversion from char to int means some nasty code would be required and I don’t have the time 🙂
Just a thought but doesn’t Craig used the “Google Syntax Highlighter for WordPress” plugin, which should also work for comments? Although as it probably doesn’t support most of these languages, maybe simply wrapping code in pre tags would be a simpler solution?
Here it is in PowerShell…
Param([char]$letter = “E”, [int]$padding=5)
$start = [int][char]”B”
$end = [int]$letter
$outerpadding = ($end – $start) + $padding
$innerpadding = -1
$lines = }
$lines
$lines[$($lines.Length-2)..0]
Ooh, nice, it ate my whole scriptblock looking for an ; Guess doesn’t work. Let’s try that again, shall we? If it’s still horrible, look here http://poshcode.org/1029
Param([char]$letter = "E", [int]$padding=5)
$start = [int][char]"B"
$end = [int]$letter
$outerpadding = ($end - $start) + $padding
$innerpadding = -1
# this line ends with an ampersand followed by a {
$lines = &{
"$(" " * $outerpadding)A"
foreach($char in ([string[]][char[]]($start..$end))) {
$innerpadding += 2; $outerpadding--
"$(" " * $outerpadding)$char$(" " * $innerpadding)$char"
}
}
$lines
$lines[$($lines.Length-2)..0]
How about a single Excel formula? (tested in Excel 2007 and OpenOffice Calc 3.0)
To create this solution,
1) Create a new workbook in Excel.
2) Format Columns A to AZ to 2.5 wide.
3) Cell A1 should contain letter A-Z.
4) Copy following formula to all cells in range Row 2 to Row 52, columns B to AZ,
=IF(ROW() – CODE($A$1) + CODE(“A”) – 2 <=0, IF(ABS(COLUMN()-27)+2 = ROW(), CHAR(CODE(“A”) + ABS(COLUMN() – 27)), “”), IF((((CODE($A$1) – CODE(“A”)) * 2) + 2) – ROW() = ABS(COLUMN() – 27), CHAR(CODE(“A”) + ABS(COLUMN() – 27)), “”))
Or just download the spreadsheet as a ZIP from here;
http://www.tigernews.co.uk/blog-twickers/Excel-Diamond.zip
Here’s another perl one, hehe… pass in the final letter, lower-case, on the command line.
#!/usr/bin/perl
$e=ord($ARGV[0]);
$d=$e-ord(“a”);
$_=” ” x $d . “a” . ” ” x $d;$\=”\n”;
sub i { return chr(ord($_[0])+1); }
while($e) {
print;push(@l,$_);
s/( )([a-z])( +)(\2)( )/i($2) . $3 . ” ” . i($2)/e;s/ a /b b/;$e=/^ /;
}
print;
while($_=pop(@l)){print;}
I feel like a sick bastard having posted that Perl solution.
So, here’s hoping the formatting is going to work… if not, please find the well formatted piece of code that this should be at this URL: http://www.sturmnet.org/download/diamond.pl.html
This is the same Perl program as before, but I thought I’d do the occasion justice and make it look nice, too. Yes, it still works!
#!/usr/bin/perl
$e=
ord ($ARGV
[0]); $d=$e-
ord( “a”);
$_=” ” x $d
. “a” . ” ”
x $d; $\=”\n”
;sub i {return
chr( ord(
$_[0] )+1);
}while ($e){
print ;push
(@l, $_);
s/( )([a-z])( +)(\2)( )/i($2) . $3 . ” ” . i($2)/e;
s/ a /b b/;$e=/^ /;}print;while($_=pop(@l)){print;}
Okay, shite. That is very interesting as well, but not what was intended. Follow the link for what it should be like!
Okay so I’ve thought about my Smalltalk code a bit and here’s an improvement. There’s no recursion in Smalltalk but you can fake it with closures. So my improved Smalltalk example goes like this…
| chars aBlock ctr |
aBlock := [ :charToPrint :spacesFromEdge : spacesBetweenChars :printCharTwice |
spacesFromEdge timesRepeat: [Transcript show: Character space ].
Transcript show: charToPrint.
printCharTwice ifTrue: [
spacesBetweenChars timesRepeat: [Transcript show: Character space ].
Transcript show: charToPrint.
].
Transcript cr.
].
Transcript clear.
chars := #($A $B $C $D $E).
ctr := 0.
chars do: [: char |
aBlock value: char value: (chars size) – ctr value: ((ctr * 2)-1) value: (char ~= $A).
ctr := ctr +1.
].
ctr := ctr – 2.
chars reverseDo: [:char |
(char ~= $E) ifTrue:[
aBlock value: char value: (chars size) – ctr value: ((ctr * 2)-1) value: (char ~= $A).
ctr := ctr – 1].
]
Since we have a few very elaborate submissions here I thought I’d just hop off the verbosity (ie. readability) bandwagon and provide an obfuscated solution in the general purpose language Python:
a=”ABCDEFGHIJKLMNOPQRSTUVWXYZ”;b=lambda c=[]:[c.append(raw_input(“Which letter? “)),c[-1]if c[-1].isalpha()and len(c[-1])==1 else b()][1].upper();b=a.index(b());a=[b*” “+”A”]+[(b-i)*” “+a[i]+(i*2-1)*” “+a[i] for i in range(1,b+1)];print “\n”.join(a+list(reversed(a))[1:])
Lord forgive me..
Robert – at least when He forgives you, he’ll also forgive me for the Perl version 🙂
Anyway, here’s another one, this time in Haskell. Indentation is important in reality, but readability shouldn’t suffer too much. And yes, I went for readability this time!
import Char
calcLine :: Int -> Int -> Int -> Int -> String
calcLine ch col line maxp =
let tch = if maxp – line == col then [chr ch] else “.” in
if col == maxp
then tch
else tch ++ (calcLine ch (col+1) line maxp) ++ tch
calcLines :: Int -> Int -> String
calcLines line maxp =
let ch = (ord ‘A’) + line in
let l = (calcLine ch 0 line maxp) ++ “\n” in
if line == maxp
then l
else l ++ (calcLines (line+1) maxp) ++ l
main = do
putStrLn “Enter a single letter”
input <- getLine
let maxp = (ord (head input)) – (ord ‘a’)
putStrLn (calcLines 0 maxp)
Overlly verbose version using iterators (sure that spacing logic could be reduced down!)
static char input;
const int AsciiA=65;
static void Main(string[] args)
{
Console.WriteLine(“Please enter a character”);
input = Console.ReadKey().KeyChar;
Console.WriteLine(“”);
int MaxWidth = Convert.ToInt32(input) – AsciiA;
int LeftSpace = MaxWidth + 1;
int Line = -1;
//forward rows
foreach (char c in GetCharForward())
{
PrintSpaces(LeftSpace);
LeftSpace -= 1;
if(c!=’A’)
{
Console.Write(c);
PrintSpaces((2 * Line));
Console.Write(c + “\n”);
}
else
{
Console.WriteLine(c);
}
Line += 1;
}
MaxWidth = MaxWidth * 2;
Line = 2;
//Back rows
foreach (char c in GetCharBackward())
{
MaxWidth -= 2;
PrintSpaces(Line);
Console.Write(c);
if (c == ‘A’) break;
PrintSpaces(MaxWidth – 2);
Console.Write(c + “\n”);
Line += 1;
}
Console.ReadKey();
}
public static void PrintSpaces(int NumSpaces)
{
string space = ” “;
for (int x = 0; x != NumSpaces; x++)
{
space += ” “;
}
Console.Write(space);
}
public static IEnumerable GetCharForward()
{
int current = AsciiA;
while (Convert.ToChar(current-1) != input)
{
yield return Convert.ToChar(current);
current += 1;
}
}
public static IEnumerable GetCharBackward()
{
int current = Convert.ToInt32(input-1);
while (Convert.ToChar(current + 1) != 65)
{
yield return Convert.ToChar(current);
current -= 1;
}
}
Okay, I guess this is my final one… same algorithm as I did in Haskell, re-implemented in Lisp. I was lazy and skipped the part with asking for input and all that – just run something like (output (calcLines 0 4)). The 4 is the maximum depth, i.e. E in this case.
(defun calcLine (ch col line maxp)
(let
((tch (if (= col (- maxp line)) (cons ch nil) (cons 46 nil))))
(if (= col maxp) tch (append (append tch (calcLine ch (+ col 1) line maxp)) tch))
)
)
(defun calcLines (line maxp)
(let*
((ch (+ line (char-int #\A)))
(l (append (calcLine ch 0 line maxp) (cons 10 nil)))
)
(if (= line maxp) l (append (append l (calcLines (+ line 1) maxp)) l))
)
)
(defun output (l)
(dolist (x l)
(format t “~A” (int-char x)))
)
With C# and recursion
public void CodeChallenge(char currentChar, char startChar, char endChar)
{
const int maxPad = 30;
string currentLine;
if( currentChar == startChar )
{
currentLine = currentChar.ToString().PadLeft( maxPad );
}
else
{
currentLine =
currentChar.ToString().PadLeft( maxPad – currentChar + startChar ) +
currentChar.ToString().PadLeft( 2 * (currentChar – startChar) );
}
Console.WriteLine(currentLine);
if(currentChar != endChar)
{
CodeChallenge( (char)(currentChar + 1), startChar, endChar );
Console.WriteLine( currentLine );
}
}
Here is my solution in Scala:
import java.text.Format
import Math.abs
object Diamond {
val A = ‘A’
private def printDiamond( ch: Char ) {
def spacingFuncFactory(maxDifference: Int, padding: Int): Int = (2 * (maxDifference – padding)) – 1
val maxDiff = (ch – A) + 1
val spacing = spacingFuncFactory(maxDiff, _: Int)
def outputLine( padding: Int, ch: Int, spaces: Int) {
def padLeft( count: Int ): String = if( count == 0 ) “” else String.format(“%1$#” + count + “s”, Array(“”))
val c = ch.asInstanceOf[Char]
println( padLeft(padding) + c + padLeft(spaces) + (if( spaces > 0 ) c else “”) );
}
def print( terminatingValue: Int, diff: Int, diffIncrement: Int, c: Int, cIncrement: Int) {
if( diff != terminatingValue ) {
val absoluteDiff = Math.abs(diff)
outputLine( absoluteDiff, c, spacing(absoluteDiff) )
print( terminatingValue, diff + diffIncrement, diffIncrement, c + cIncrement, cIncrement )
}
}
print( 0, maxDiff, -1, A, 1)
print( maxDiff + 1, 2, 1, (ch – 1), -1)
}
def main( args: Array[String] ) {
if (args.length == 0 || args(0).length == 0 ) {
println(“Diamond: No argument supplied”)
return
}
val firstChar = args(0)(0)
if( firstChar ‘Z’) {
println(“Diamond: The first character of the argument supplied is outside of the range of A to Z”)
return
}
printDiamond( firstChar )
}
}
Dammit! looks like the blog engine has stripped out some stuff. The last “if” statement is checking to make sure the first char is between “A” and “Z”.
Cheers
Barry
Did a SQL 2008 one that can take any char string
DECLARE @Input AS VARCHAR(MAX)
DECLARE @Result AS VARCHAR(MAX)
DECLARE @Length AS INT
DECLARE @x AS INT
SET @Input = ‘ABCDE’
SET @Length = LEN(@Input)
SET @Result = ”
SET @x = 0
WHILE ( @x 0 )
SELECT @Result = @Result + SPACE(( @x * 2 ) – 1) + SUBSTRING(@Input, @x + 1, 1)
SELECT @Result = @Result + CHAR(13) + CHAR(10)
SET @x = @x + 1
END
SET @x = @x – 1
WHILE ( @x >= 0 )
BEGIN
SET @x = @x – 1
SELECT @Result = @Result + SPACE(@Length – @x) + SUBSTRING(@Input, @x + 1, 1)
IF ( @x > 0 )
SELECT @Result = @Result + SPACE(( @x * 2 ) – 1) + SUBSTRING(@Input, @x + 1, 1)
SELECT @Result = @Result + CHAR(13) + CHAR(10)
END
PRINT @Result
Better format and working copy/paste SQL2008 solution
http://pastebin.com/f794d1456
A clojure version implemented here. Unlike some of you, I went for legibility over terseness. Despite this it’s still only 44 lines of code (including function docs) of clear concise clojure:
http://paste.lisp.org/display/78674
It’s also 100% thread safe, easily parallelisable (though to do so would doubtless incure more overhead than it’s worth). The program has no mutable state and has been written in a purely functional style, with only the function to print having a side effect.
I’m afraid my APL-Solution may not display right (although Unicode, so it should)
(Using Dyalog APL with IO=1 and ML=0):
{{{⍵⍪1 0↓⊖⍵}⍵,0 ¯1↓1⌽⌽⍵}↑(⎕a⍳⍵){(⍳⍺)⌽(⍺,⍺)↑(⍺,1)⍴⍵}⎕a}’E’
see screenshot here: http://twitpic.com/3ecda
BTW: should I win, I have a friend in UK whose postal address you could use to mail the shirt 🙂
Oh, and to have it loop over a set of characters, we just use the “each”-operator:
{{{⍵⍪1 0↓⊖⍵}⍵,0 ¯1↓1⌽⌽⍵}↑(⎕a⍳⍵){(⍳⍺)⌽(⍺,⍺)↑(⍺,1)⍴⍵}⎕a}¨’BAAS’
🙂
Oh no, that last bit was no APL!
To clarify: with “that last bit” I was referring to the chars : – ) which were replaced with the smiley!
Ok I have to represent the Sybase SQL users out there…
CREATE PROCEDURE DBA."sp_generate_diamond" ( IN apex_char CHAR(1) )
BEGIN
DECLARE OFFSET INTEGER;
DECLARE DISTANCE INTEGER;
DECLARE DIRECTION INTEGER;
DECLARE CURRENTLINE CHAR(150);
SET OFFSET = ASCII(apex_char)-ASCII(‘A’);
SET DISTANCE = 0;
SET DIRECTION = 1;
WHILE ( DISTANCE >= 0 ) LOOP
SET CURRENTLINE = REPEAT( ‘ ‘, OFFSET-DISTANCE ) || CHAR(ASCII(‘A’)+DISTANCE);
IF( DISTANCE > 0 ) THEN
SET CURRENTLINE = CURRENTLINE || REPEAT( ‘ ‘, (DISTANCE*2)-1 ) || CHAR(ASCII(‘A’)+DISTANCE);
END IF;
MESSAGE CURRENTLINE TO CLIENT;
IF( DISTANCE = OFFSET ) THEN
SET DIRECTION = -1;
END IF;
SET DISTANCE = DISTANCE + DIRECTION;
END LOOP
END
Call it using the following statement:-
CALL dba.sp_generate_diamond( ‘E’ );
And the code is displayed in the “Statistics” window in Interactive SQL
Incase of formatting mess see this… http://pastebin.com/m41d6ea23
I was amazed by how many lines of code some languages require to solve this. The whole problem can be done in a single line of APL. I decided to document one solution on the APL Wiki web site:
http://aplwiki.com/Studio/LetterDiamonds
(This solution would only need 42 characters if you choose single-letter variable names)
Diamond in Scala http://pastebin.com/f56cf1a54
Wot no Ruby?
diff = ARGV.shift[0].ord - ?A
[(0..diff),(0...diff).to_a.reverse].each do |range|
range.each do |i|
s = (" "*(diff-i)) + (?A+i).chr + (" "*i)
puts s + s[0..-2].reverse
end
end
Not elegant in any sense, but it works.
Wimps here it is for the Apple II lovingly assembled with the miniassembler if you paste this into the applewin emulator:
CALL -151
300:A0 0E B9 00 03 F0 17 20
:ED FD C8 4C 02 03 C5 CE
:D4 C5 D2 A0 D3 C9 DA C5
:A0 C1 AD DA BF 00 20 0C
:FD C9 C1 30 04 C9 DA B0
:09 20 2D FF 20 8E FD 4C
:00 03 29 1F AA CA 86 08
:A2 FF 86 07 E8 E8 86 09
:A9 C1 85 06 20 8E FD A6
:08 D0 06 C6 09 C6 09 D0
:03 20 4A F9 A5 06 20 ED
:FD C9 C1 F0 0A A6 07 20
:4A F9 A5 06 20 ED FD 20
:8E FD 38 A5 08 E5 09 85
:08 18 A5 07 65 09 18 65
:09 85 07 18 A5 06 65 09
:85 06 C9 C0 D0 C1 60
300g
You’ll get a loverly prompting diamond printer.
I’m officially not feeling like a sick bastard anymore now.
Here is a version in J: (see http://www.jsoftware.com)
firstletters=: (65}.a.)&(>:@i. {. [)
shape=: {.&>~ # {. i:@# NB. shape letters
hflipcat=: ,. |.@}:”1 NB. flip horizontally & catenate
vflipapp=: , |.@}: NB. flip vertically & append
vflipapp hflipcat shape firstletters ‘E’
Or as one function (verb):
diamond=: vflipapp@hflipcat@shape@firstletters
diamond ‘E’
If anyone wanted it as one line of code:
(,|.@}:)(,.|.@}:”1)({.&>~#{.i:@#)(65}.a.)(>:@i.{.[)’E’
Note that the blog is “smart” and converts single and double quotes into “pretty” quotes.
As a result if you copy the code back to a J session you will need to replace the “pretty” versions with their simple counterparts for the code to work.
Here’s an APL2 solution that is not a one-liner.
(I like comments better than brevity.)
∇LETTERDIAMOND[⎕]∇
∇
[0] MAT←LETTERDIAMOND ARG;LIST;⎕IO
[1] ⎕IO←0 ⍝ Set the index origin
[2] LIST←⎕AF(⎕AF ‘A’)+0,⍳-/⎕AV⍳ARG,’A’ ⍝ Build list of letters
[3] MAT←⊃(-⍳⍴LIST)↑¨LIST ⍝ Build upper right part
[4] MAT←(⌽MAT),1↓[1]MAT ⍝ Add upper left part
[5] MAT←MAT,[0]1↓[0]⊖MAT ⍝ Add lower half
∇ 2009-04-16 22.50.50 (GMT-4)
LETTERDIAMOND ‘E’
A
B B
C C
D D
C C
B B
A
BTW This sample is encoded using Unicode codepoints for the APL characters. If you can not read it here, I suggest you copy and
paste it into an editor and use Adrian Smith’s APL385 Unicode
font which is available here: http://www.vector.org.uk/?area=fonts.
David Liebtag
IBM APL Products and Services