|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionMany people hate Perl because of its frightening syntax. While it's true that many Perl scripts border on the unreadable, some of Perl's operations are extremely handy. Two of them are the The code here is based on .NET framework/C# 2.0. The new framework version (in beta, as of this writing) provides good support for similar functions. For 1.x series of .NET, implementing this sort of operators is possible, but hard to do in a generic manner; you'll be needing a good dose of interfaces, and it will be hard to integrate with the existing types ( BackgroundLists in PerlFirst, we must introduce ourselves to the list in Perl. It's roughly the equivalent of a one-dimensional array in most other languages. Perl's relatively weak typing means that the list element can be anything; integers, strings, references ("pointers") to structures, and so on. In these examples, we'll only look at the basics. The Perl list is denoted using a The map operatorPerl's For example, you can do the following: my @numbers = (1, 2, 3, 4);
my @doubles = map { $_*2 } @numbers;
print @doubles; # prints 2468
The equivalent construct using my @numbers = (1, 2, 3, 4);
my @doubles;
foreach (@numbers) {
push @doubles, $_*2;
}
print @doubles;
In C#, the The grep operator
my @names = ("John", "Mike", "Jane", "Adam");
my @oNames = grep { substr($_, 0, 1) eq 'J' } @names;
print @oNames;
With a little thought, I think you can figure out what the code above does. Yeah, it prints out those names whose first character ("1 character long substring from index position 0") equals 'J' - that is, John and Jane. The C# approachI've been longing for those two Perl operations for quite some time. When I first read the C# 2.0 spec, I realized the potential both generics and anonymous methods had for this purpose. However, I later noted that Framework 2.0 already contains fairly good support for these operations in the form of a couple of new methods. While the terse syntax of Perl will be missed, using The map equivalent: ForEachTyped arrays and the new private static void PrintNumber(int num) {
Console.WriteLine("The number is " + num);
}
public static void Main() {
int[] list = {1, 2, 3, 4};
Array.ForEach(list, new Action<int>(PrintNumber));
}
One of the greater new C# enhancements is the ability to define methods anonymously. So we can actually get rid of the public static void Main() {
int[] list = {1, 2, 3, 4};
Array.ForEach(
list,
delegate(int num) { Console.WriteLine("The number is " + num); }
);
}
The missing part here is the lack of return type in the Action delegate; you can't easily use it to construct a new list or array. So, let's construct some of our own tool code to help: public delegate T MapAction<T>(T item);
public static T[] MapToArray<T>(T[] source,
MapAction<T> action) {
T[] result = new T[source.Length];
for (int i = 0; i < source.Length; ++i)
result[i] = action(source[i]);
return result;
}
So, we now have a public static void Main() {
int[] list = {1, 2, 3, 4};
int[] doubled = MapToArray(list,
delegate(int num) { return num*2; });
foreach (int i in doubled) Console.WriteLine(i);
}
... and have it print out 2, 4, 6 and 8. That's it! Except that this isn't as flexible as Perl's public delegate DstType MapAction<SrcType, DstType>(SrcType item);
public static DstType[] MapToArray<SrcType, DstType>(
SrcType[] source,
MapAction<SrcType, DstType> action) {
DstType[] result = new DstType[source.Length];
for (int i = 0; i < source.Length; ++i)
result[i] = action(source[i]);
return result;
}
We now have a whole lot of two type parameters - which I've also named public static void Main() {
string[] files = { "map.pl", "testi.cs" };
long[] fileSizes =
MapToArray<string,long>(
files,
delegate(string file) { return new FileInfo(file).Length; }
);
for (int i=0; i < files.Length; ++i)
Console.WriteLine(files[i] + ": " + fileSizes[i]);
}
Yep, that example maps an array of filenames ( The grep equivalent: FindAllAfter having gone through all the trouble of implementing the map operation, implementing a int[] list = {1, 2, 3, 4, 5, 6};
int[] even = Array.FindAll(list,
delegate(int num) { return num%2 == 0; });
foreach (int i in even) Console.WriteLine(i);
... and yes, the code above prints 2, 4 and 6. That's it for Concluding remarks
The map operations described above are far from perfect. They're restricted to arrays both on input and output. It would be fairly simple to make them swallow History2004-07-23: Initial version released.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||