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!
Want to use code from this post? Check out the license.
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 :)