One of the things to keep in mind when learning a new programming language is that it isn’t enough to learn the syntax and semantics of various language features you are unfamiliar with. Just as important is learning the idioms and way of thinking that goes with these language features. On reddit, ubernostrum pointed out that my usage of
if all_links.get(url) == None:
was jarring to read as a Python programmer when compared to the more idiomatic
if url not in all_links:
Of course this is just a stylistic issue but his point is valid. A similar thing happened with regards to other aspects of my recent post entitled Does C# 3.0 Beat Dynamic Languages at their Own Game?
I argued that type inferencing and anonymous types in C# 3.0 did not offer the same degree of functionality that tuples and dynamic typing did when it came to processing intermediate values in a computation without requiring nominal types (i.e. named classes) to hold these values.
Specifically I had the following IronPython code,
IronPython Code
for item in filteredItems:
vote = (voteFunc(item), item, feedTitle)
#add a vote for each of the URLs
for url in item.outgoing_links.Keys:
if all_links.get(url) is None:
all_links[url] = []
all_links.get(url).append(vote)
# tally the votes, only 1 vote counts per feed
weighted_links = []
for link, votes in all_links.items():
site = {}
for weight, item, feedTitle in votes:
site[feedTitle] = min(site.get(feedTitle,1), weight)
weighted_links.append((sum(site.values()), link))
weighted_links.sort()
weighted_links.reverse()
The key things to note about the above code block are (i) the variable named vote is a tuple of three values; the numeric weight given to a link received from a particular RSS item, an RSS item and the title of the feed Python and (ii) the items in the tuple can be unpacked into individual variables when looping over the contents of the tuple in a for
loop.
When I tried to write the same code in C# 3.0 with a vote variable that was an anonymous type, I hit a road block. When I placed instances of the anonymous type in the list, I had no way of knowing what the data type of the object I’d be pulling out of the list would be when I wanted to extract it later to tally the votes. Since C# is statically typed, knowing the type’s name is a requirement for retrieving the objects from the list later unless I planned to interact with them as instances of System.Object and access their fields through reflection (or something just as weird).
So in my C# 3.0 solution I ended up creating RankedLink and Vote types to simulate the functionality I was getting from tuples in Python.
However it turns out I was using anonymous types incorrectly. I tried to take a feature that was meant to be coupled with C# 3.0’s declarative Language Integrated Query (LINQ) and use it in the traditional imperative loop constructs I’ve been familiar with since my days programming in C.
Ian Griffith’s set me straight with his blog post entitled Dare Obasanjo on C# Anonymous Types where he showed how to use anonymous types to get the solution I wanted without having to create unnecessary named types to hold intermediate values. Ian’s code is shown below
C# 3.0 Code
// calculate vote for each outgoing url
var all_links = from item in items
from url in item.OutgoingLinks.Keys
group item by url into itemUrlGroup
select new
{
Url=itemUrlGroup.Key,
Votes=from item in itemUrlGroup
select new
{
Weight=voteFunc(item),
Item=item,
FeedTitle=feedTitle
}
};
// tally the votes
var weighted_links = from link_n_votes in all_links
select new
{
Url=link_n_votes.Url,
Score=(from vote in link_n_votes.Votes
group vote by vote.FeedTitle into feed
select feed.Min(vote => vote.Weight)
).Sum()
} into weighted_link
orderby weighted_link.Score descending
select weighted_link;
As you can see, Ian’s code performs the same task as the Python code does but with a completely different approach. The anonymous types are performing the same function as the Python tuples did in my previous code sample and there is no need to create RankedLink and Vote types to hold these intermediate values.
What I find interesting about this is that even though I’ve been using C# for the past five or six years, I feel like I have to relearn the language from scratch to fully understand or be able to take advantage the LINQ features. Perhaps a few stints as a SQL developer may be necessary as well?