Selecting The Seven Languages In Seven Weeks Winners
About a week or so ago, I finished working my way through Seven Languages in Seven Weeks by Bruce Tate. To celebrate my mostly triumphant reading of the book (shakes fist at Haskell, Day 3), I wanted to give away a few copies. But, in order to select the winners of the book, I wanted to have a little fun and try to play with the Mapping concepts that were often discussed within the book.
Given an array of players (those that submitted sample code on my wrap-up), I created an ArrayMap() function and a function that would randomly return a given value. The idea was to continue to reduce the collection of players until only 2 were left.
<!--- Set up the array of players. --->
<cfset players = [
"Wilkins",
"Mike",
"Seth Stone",
"Guganeshan.T",
"Joshua Caito",
"Joe Zack",
"Andrew",
"Rich",
"Hidde-Jan",
"dotnetCarpenter",
"Adam Coffman",
"Owen Pellegrin",
"Ross Pfahler",
"Marty",
"Jared Cacurak",
"Morten",
"Greg Mefford",
"Tracy Harms",
"Eric Mitchell",
"Jesse Riggins",
"Danny Philayvanh",
"Sharagoz",
"Joel Neley",
"Adam",
"Nelle",
"Matt Gutting",
"Cade Cannon"
] />
<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->
<!---
Define a map function that maps one array onto another using
the given function. When mapping, the following values will be
handles as such:
- Array - will be flattened and merged into the resultant array.
- Value - will be added to the resultant array.
- VOID - will not be added ot the resultant array.
--->
<cffunction
name="arrayMap"
access="public"
returntype="array"
output="false"
hint="I map one array onto another array.">
<!--- Define arguments. --->
<cfargument
name="originalArray"
type="array"
required="true"
hint="I am the array being mapped."
/>
<cfargument
name="mapper"
type="any"
required="true"
hint="I am the function used to perform the mapping."
/>
<!--- Define the local scope. --->
<cfset var local = {} />
<!--- Define an array to hold the results of the mapping. --->
<cfset local.results = [] />
<!--- Loop over the array so we can map each value. --->
<cfloop
index="local.originalValue"
array="#arguments.originalArray#">
<!--- Hand this value off to the mapper function. --->
<cfset local.mappedValue = arguments.mapper(
local.originalValue
) />
<!---
Check to make sure that a value exists. If it doesn't,
it means that VOID was returned; as such, no mapped
value will be added to the results.
--->
<cfif structKeyExists( local, "mappedValue" )>
<!---
We know we have a mapped value, so let's not figure
out how we are merging it into the results array.
Arrays will be flattened; single values will be
added as such.
--->
<cfif isArray( local.mappedValue )>
<!--- Add each value of the array individually. --->
<cfloop
index="local.mappedSubValue"
array="#local.mappedValue#">
<!--- Add the flattened values. --->
<cfset arrayAppend(
local.results,
local.mappedSubValue
) />
</cfloop>
<cfelse>
<!--- Simply add the returned value. --->
<cfset arrayAppend(
local.results,
local.mappedValue
) />
</cfif>
</cfif>
</cfloop>
<!--- Return the resultant array. --->
<cfreturn local.results />
</cffunction>
<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->
<!---
Now that we have our map function defined, we need to create a
function that actually provides the mapping logic. For our
purposes, we're going to keep mapping the players array onto a
new array until we only have 2 people left. That means that our
mapper function will be popping people out of the collection.
--->
<cffunction
name="returnRandom"
access="public"
returntype="any"
output="false"
hint="I randomly return the given value or VOID.">
<!--- Define arguments. --->
<cfargument
name="value"
type="any"
required="true"
hint="I am the value being returned (maybe!)."
/>
<!---
Randomly decide to return this value or not. If it is
returned, it will be mapped onto the results array; if it
is not, it will be removed.
--->
<cfif (rand() gt .25)>
<!--- Return the given value. --->
<cfreturn arguments.value />
<cfelse>
<!--- Return void. --->
<cfreturn />
</cfif>
</cffunction>
<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->
<!---
Now that we have all of our mapping functions in place, let's
reduce our players collection down to a collection of 2. As we
map, there is a chance that we will drop below 2. In that case,
we'll have to just start over again.
--->
<cfoutput>
<!---
Keep looping until we stop. If we get a collection of 2,
we will execute an explicit Break command.
--->
<cfloop condition="true">
<!--- Copy our players array (set by VALUE). --->
<cfset winners = players />
<!--- Output the staring length. --->
Starting Length: #arrayLen( winners )#<br />
<!---
Keep reducing the collection until it is less than
or equal to 2 (we are hoping it is 2).
--->
<cfloop condition="(arrayLen( winners ) gt 2)">
<!--- Reduce the collection randomly. --->
<cfset winners = arrayMap(
winners,
returnRandom
) />
<!--- Output the new length. --->
Current Length: #arrayLen( winners )#<br />
</cfloop>
<!--- Check to see if the collection contains 2 values. --->
<cfif (arrayLen( winners ) eq 2)>
<!---
We have reduced properly. Stop the reduction loop
as we have found our winners.
--->
<cfbreak />
</cfif>
</cfloop>
<hr />
<!--- Output the winners. --->
Winner: #winners[ 1 ]#<br />
Winner: #winners[ 2 ]#<br />
</cfoutput>
Since the reduction mapping might result in an array of length less than 2, I had to continually loop until the final array contained exactly two elements. When I ran the above code, I got the following page output:
Starting Length: 27
Current Length: 18
Current Length: 15
Current Length: 13
Current Length: 11
Current Length: 11
Current Length: 5
Current Length: 3
Current Length: 3
Current Length: 1
Starting Length: 27
Current Length: 21
Current Length: 16
Current Length: 10
Current Length: 8
Current Length: 8
Current Length: 7
Current Length: 7
Current Length: 7
Current Length: 5
Current Length: 1
Starting Length: 27
Current Length: 22
Current Length: 21
Current Length: 13
Current Length: 10
Current Length: 6
Current Length: 6
Current Length: 5
Current Length: 5
Current Length: 3
Current Length: 3
Current Length: 2Winner: Jared Cacurak
Winner: Matt Gutting
As you can see, the reduction mapping had to run 3 times before it ended up with an array of length 2. Of course, I know there are much more efficient ways of doing this; but, efficiency wasn't really the point - I just wanted to have some fun with it.
Congratulations to Jared and Matt! You're about to get seven languages deep!
Reader Comments
Stupid ColdFusion!
I thought the point of your blog was to make me like ColdFusion, now I am not so sure it will every happen :)
/A Loser
@Morten,
Ha ha ha, the silver-lining to all of this is that if you use ColdFusion, you're already a winner :P
Feels good to see the name 'inside' coldfusion code ^_^
Congrats to winners.
@Guganeshan,
Thanks for participating my friend :)
Grats to the winners!
Thanks again for your creativity with the contest Ben. It would be cool to do something like this every once in a while even if there is no prize involved :)
@Andrew,
Always a pleasure to get smart people involved in good conversation :)
@Ben: Your mom is very informative. Thanks.
;)
@Todd,
Ha ha ha, well played :)