25 Posts Tagged 'Perl'
Let's parse
Is there anything more fun than parsing strings? I submit to you that there is not. I'm currently reading my way through Parsing Techniques - A Practical Guide, which has a first edition free online. (I'm hoping Santa brings me a copy of the 2nd edition this year.)
This is a good book, with enough math to be rigorous but not so much that it's completely unreadable. It starts from the absolute basics ("What's a grammar?") and goes through the Chomsky hierarchy and then dives into parsing techniques in great detail, in a language-agnostic way.
Languages and grammars are fascinating. In high school I studied Spanish, French, Latin and German, largely in my spare time. When I was 16, if people asked what I wanted to do for a living, I said "translator".
The plan to become a translator failed partly because the quality of my early education was horrendous and partly because mastering a language is extremely difficult and at 16 I wasn't motived enough. And then computers showed up in my life, which gave me a never-ending supply of languages to play with, while being fun (and profitable) in so many other ways. But I still took two years of Japanese classes in college for no reason other than enjoyment, and I'm still trying (and failing) to learn Japanese in my spare time 8 years later.
Perl was my first favorite language probably for no reason other than regular expressions. I can understand how people call PCRE syntax line-noise, but to me it's beautiful line noise. I live and breathe regular expressions nowadays. My favorite CS class in college was one where we went through and laboriously built finite-state automata and pushdown automata and Turing machines. Seeing the equivalence of these simple machines with the different classes of grammars was a huge epiphany. Such a simple concept with such huge consequences.
Dijkstra said:
Besides a mathematical inclination, an exceptionally good mastery of one's native tongue is the most vital asset of a competent programmer.
I strongly agree with that sentiment. People tell me at times that I'm good at written communication. I have my doubts, and anyways I find it funny because I'm so terrible at verbal communication. I think if I have any success at writing, it's because I view writing as a mechanical process.
I told a prof in college once that I felt like my papers wrote themselves once I had an idea in mind. There are rules of grammar and style, and you learn them and follow them, or break them deliberately if you have a good reason to. You write some prose, then you debug it until it "works" mentally. I don't care about typos and I split infinitives and comma-splice on purpose, but ambiguous or awkward phrases usually stand out to me like compiler bugs in my brain.
What's more important than language? Few things. Language is important enough to be nearly hard-wired into our brains. Children learn it instinctively. Human beings can still easily and effortlessly out-perform the best supercomputer at the task of parsing and interpreting speech. We think in words. The programming languages computers understand are dirt-simple by comparison, but writing code still feels like writing "thoughts for the computer" sometimes.
There are very few times you'll hear me say "What a wonderful world we live in". But one of those times is when I have the opportunity to explore an area of study like language. It's such an enjoyable experience to struggle and try to master such a thing. It's an amazing universe where we have these weird little rules and they work and we can understand them and manipulate them and produce things with them.
Markdown
How do you write a parser in a functional language like Clojure? (That's a rhetorical question.) There are parser libraries for Haskell I could use as reference but they're still a bit over my head at this point.
The original Markdown.pl parser in Perl uses global hashes and regex-mangles strings directly. I could actually duplicate this exactly in Clojure, because Clojure isn't purely functional. But I'm trying to do it in a more functional way, and so far it's working out OK.
One of the bad things about Markdown is that perhaps because it's originally implemented in Perl as a bunch of regex-replacements on a string, and not as a real parser with a proper grammar, all of the implementations of Markdown in various other languages give slightly different results. So much so that someone wrote a website just to compare different implementations of Markdown against each other. So now writing a parser in Clojure, I face the difficulty of which behavior I want to duplicate. Some things Markdown does less than ideal, but I think I have to err on the side of replicating the original. One implementation, Pandoc, claims to be "more accurate" than Markdown.pl, but Pandoc seems to purposefully break from things that are laid out explicitly in the specification, which is bad.
Perl6 features borrowed from Lisp
Via PerlMonks I found a couple of articles discussing in good detail some of the new features of Perl6.
Perl6 steals even more things from Common Lisp than Perl5 did: it has multimethods / multiple dispatch for example, which is a huge plus. Via this interview with Damian Conway we learn that Perl6 will also have named, optional, and "rest" parameters to subs, just like in CL. That's also a good thing; CL's parameter-passing styles are nice, and it's awesome how you can combine them. Certainly better than Perl5 (but everything is better than Perl5). There's also apparently special Perl6 syntax for applying functions to lists and currying functions, and weird Capture objects to explicitly deal with multiple-value returns from subs. Good stuff.
Perl6 is also apparently taking first-class functional objects to an extreme; blocks, subs, and methods are all objects and there are all kinds of metaprogramming hooks to screw around with them. This is one area where Ruby is just a little bit lacking: functions and methods aren't quite first-class enough in Ruby. Most people seem to pass around symbols / names of methods rather than pass around methods as objects themslves. Anonymous blocks are used liberally but mostly via yield, limiting you to one block per method and largely hiding away the block objects themselves.
I'm honestly a bit excited about Perl6, but largely as a curiosity or new toy to play with. It is kind of interesting how languages keep creeping more and more toward Common Lisp. If Perl is a nicer-looking Common Lisp which I can edit properly in Vim, it'll be almost a dream come true; I hate Emacs and Common Lisp tends to be butt-ugly. (Not talking about the parens, mostly about the verbosity and cruft and inconsistencies. Larry Wall famously said that Common Lisp looks like (paraphrased) "oatmeal with toenail clippings mixed in". Perl is certainly at the other extreme.)
http://rakudo.org/ is a good site for keeping up on Perl6 news. It's pretty active. Here's hoping we see a real release of Perl6 someyear.
Wish list
What's the Common Lisp version of Perlmonks or Ruby-forum? I have yet to find it.
comp.lang.lisp is largely crap. 50% of the traffic on that list is spam about shoes and fake watches. The other half is equally split between:
- People debating tiny, silly semantic points of the Common Lisp Hyperspec.
- People stuck in the 70's or 80's, talking about the good old days, ruminating about Lisp history.
- Flame wars.
- New people asking for help. Some get good honest advice and helpful answers, many are flamed and ridiculed into next week if they even hint that they dislike the parentheses.
The Common Lisp community (if you can call it that) is a bunch of really smart guys, but they all live isolated in hermit shacks up in the mountains and they spend their time doing magic tricks with Lisp that few people ever see, and if you wander too close they throw rocks at you.
What's the Common Lisp equivalent of perldoc or rdoc? We have the Hyperspec. It's an impressive document, but it's a bunch of painful HTML that looks like it was created in the early 90's, probably because it was. It reads like a dusty, dry, technical document probably because it is. What it's not, is friendly or easily readable.
Perl has CPAN, Ruby has rubygems, what does Lisp have? Either a hand-rolled system definition script, or if you're lucky an ASDF install file. ASDF is the semi-standard Lisp way of installing libraries, except that it doesn't quite work in Windows, it doesn't check dependencies or handle different versions of a package very well, and it doesn't work the same on all Lisp implementations. Many people in the so-called community think it's not very good.
The fellow running Lispcast makes another good point. Where can you download Lisp? It's not obvious.
You could say "OK Brian, good idea, now get to work!" The problem is that even if I had the time or willpower, I'm not the smartest guy in the world. I honestly don't think I could design and run and maintain a CPAN. And even if I did, would anyone use it? But I do know that there ARE plenty of smart, enthusiastic people using Lisp. Yet high-quality friendly code is largely not being produced.
Peter Christensen wrote about "langauge snobs" and the importance of community. One point made is that some really ugly, horrific languages have been extremely successful simply because they've been accessible and fun. An example given is the scripting language in Second Life, which has over 2.5 billion lines of code written in by tens of thousands of amateurs and has accurately modeled a realistic 3D environment with thousands of users at any given time. All in an ugly language some guy invented AND implemented in one week. The developers admit that the language is total crap, but it doesn't matter. 1) It has very good and accessible documentation, 2) it has a very newbie-friendly community, and 3) and it's easy to pick up, throw together some code and get immediate results. Three things Common Lisp lacks.
This is something I've said myself many times: an active, supportive, enthusiastic community is essential for the health of any programming language. Common Lisp simply doesn't have one and it's a shame.
I still secretly hope that Clojure or NewLisp or Arc turn out to be a huge success. They are the kinds of things Lisp needs today.
Perl 6
I found this Perl6 imaginary timeline hilarious. Sadly we're in 2008 and I don't think that blue line is quite as high as it was projected to be. Though it appears that development is still slowly chugging along. I still check up on Perl 6 once every six months or so. You can actually download perl6 (called rakudo?) and run it nowadays.
It's been a long time since I've used Perl for anything. If Perl 6 ever sees the light of day in a big way, I'm sure I will hunt down the Perl 6 bandwagon and chain myself to the back of it. Perl was my first religion.
I don't want to consider myself fickle, rather curious and eager to try and learn new things. But honestly, fickle may be closer to the truth. Already my Lisp enthusiasm is starting to wear off, largely from the impracticality of writing anything in Lisp (in spite of good book titles to the contrary). I've written a bunch of things in the past few months, but I never even considered Common Lisp for any of them. Mostly because I was being paid to write them, and I don't think people want to pay me to screw around with something that's going to take me 10x as long as using a language I know already.
I believe the main way I came to know and love Perl and later Ruby was through all the little 5-minute throwaway scripts I wrote to get my job(s) done. Those little scripts led to bigger scripts, which led to even bigger scripts. Emotionally it's satisfying to actually solve a problem in a language, even a small problem. Not so easy to do in Lisp; it doesn't seem to lend itself well to scripting. Lisp is nice if you've come up with the perfect abstraction for your program and want to implement it exactly like you're thinking of it. Most of the time, by the time I think up the perfect abstraction, I'd already be done if I'd written it in straightforward Ruby to begin with.
And, to rant briefly, in Ruby I wouldn't have to write my own function to fetch all the keys of a hash, or print a formatted date string, or any of the other countless little holes in Lisp that are nicely filled in Ruby by all the available libraries. I've said it before and I'll say it again: a large active community is one of the biggest necessities for a healthy programming langauge. It really doesn't seem like many people in the Lisp community give a crap about getting lots of new people to use Lisp. Or if so, I never hear much about it, compared with other communities. There's no reason a language can't be really great and powerful, and still accessible to the masses. That's one of Ruby's strengths. You don't have to read a few hundred pages of hyperspec and learn 50 years of history and spend a week and half setting up an arcane SLIME/Emacs environment to start writing Ruby.
But yeah, Perl 6. I want it to succeed, for entirely emotional reasons. I want a new toy.
Lisp ramblings
Two posts in one, just because I can.
Rambling #1: One thing that makes a language like Ruby easy to read is that foo.bar always has the same meaning: object.method.
In Common Lisp you don't have that. One of CL's strengths is that everything looks the same, it's all s-expressions. However a cost of this is that you can't tell just by looking whether (foo bar) is:
- A special operator foo, that does something with bar (maybe assigning it a value, maybe conditionally evaluating it, who knows).
- A system-defined function call foo, where bar is evaluated and the result is passed in as an argument, and which you can't redefine because it's in the
COMMON-LISPsystem package. - A user-defined function call foo, where bar is evaluated and the result is passed in as an argument, and which you can re-define if you want.
- A method call foo, where bar is possibly an object used to dispatch the method call (or maybe not).
- A macro foo, which can produce code that does practically anything, or nothing at all, with bar.
- Other?
Along the same lines, even if you know for sure that (foo bar baz) is a
method call, you can't tell by looking whether bar, or baz, or both, are
objects which determine how the method call is dispatched. You have to look
it up in documentation or inspect the method somehow. As far as I know.
(EDIT: Slime has lots of tools for doing this kind of inspection., as was pointed out to me by Ivar.)
This is a good feature of Common Lisp, in terms of flexibility and power; multimethods are great, the consistency and lack of syntax and precedence is great in many ways. But it can be a bad thing in terms of easy readability. It's a bit of a tradeoff.
Ruby code like arr.reverse.sort.join(',') just flows right along. And it's highly predictable. I know reverse is a method call, and it's defined in whatever class arr is an instance of (or a mixin of that class, or a meta-class I guess), and it returns something that has a sort method. And I can redefine any of those things.
Common Lisp non-working pseudo-code: (join (sort (reverse arr)) ","). Not as much fun to read, and oops, in this case SORT is a system-defined standard function, so if I want to write this I'll have to go with MY-SORT or SORT2 to avoid collisions (which you see a lot of in Lisp examples and literature).
Rambling #2: Way back in 2006, I decided to learn Common Lisp because I'd heard such nice things about it. I ended up abandoning that project very quickly (only to try again late in 2007, and stick with it this time). One reason (among many many others) that I gave up was the fact that it took me a good hour or two even to figure out how/where to download and install Common Lisp.
I remember going to a terminal and searching the Portage tree for packages matching the name "common lisp". Zero results. OK, how about just "lisp". One result I got back was clisp, which at that time, based on the name, I assumed was "just another name for the Common Lisp interpreter". However a search of the Gentoo forums revealed that a lot of people were using SBCL, or CMUCL, or many others. And those things were "Common Lisp" too? How confusing.
Little did I know that there is no THE Common Lisp. There are a bunch of implementations of the Common Lisp standard (or varying parts of the standard). To a newbie, this is somewhat confusing. Which one should you pick? What the heck is the difference? If it's a standard, does it even matter which you pick? So before you can even start writing code, you get to treat yourself to a history lesson to learn why there are all these different implementations, and then go on a fact-finding mission to try to find an implementation that's right for you.
It wouldn't matter, if the standard was complete enough that it covered all of what you'd want to do. But the standard is old, and arguably out-of-date. And it doesn't cover things like threads, sockets, POSIX utilities, etc. that you're really likely to use. And if something isn't in the standard, you can bet good money that every implementation does that thing in its own way, or not at all. A lot of sufficiently complex code in Common Lisp will demand that you go to great lengths to get it to run on different implementations, judging by some of the library code I've read.
Consider CL-FAD. A function DELETE-DIRECTORY-AND-FILES is used to (you guessed it) recursively delete a directory and its files. Here are the guts of that function:
(cond ((directory-pathname-p file)
#+:lispworks (lw:delete-directory file)
#+:cmu (multiple-value-bind (ok err-number)
(unix:unix-rmdir (namestring (truename file)))
(unless ok
(error "Error number ~A when trying to delete ~A"
err-number file)))
#+:scl (multiple-value-bind (ok errno)
(unix:unix-rmdir (ext:unix-namestring (truename file)))
(unless ok
(error "~@<Error deleting ~S: ~A~@:>"
file (unix:get-unix-error-msg errno))))
#+:sbcl (sb-posix:rmdir file)
#+:clisp (ext:delete-dir file)
#+:openmcl (ccl:delete-directory file)
#+:cormanlisp (win32:delete-directory file)
#+:ecl (si:rmdir file)
#+(or :abcl :digitool) (delete-file file))
(t (delete-file file)))
What's going on there? All those #+ and #- lines are kind of like the Common Lisp equivalent of C preprocessor macros. #+ and #- hide (or include) a line of code from the view of the compiler, depending on which implementation you're using. Ugh.
So in this case, you have what is a pretty simple function (1-5 lines of code, in each implementation), but you get to have the fun of writing it TEN TIMES, all woven together like spaghetti. There are many other such compatibility layers.
Kudos to people who go to the effort of writing portable code like this, but I don't see why it should be necessary. This strikes me as a senseless and wasteful duplication of effort. Not just on the part of the people writing code to run on all these implementations, but also on the part of the people working on the implementations themselves. Why not join forces and make one big mature official Common Lisp for everyone to use? What benefit is there to having so many implementations? Does the benefit outweigh the amount of work someone has to do to write portable code to share with the community? Maybe there's a good reason for it that I'm not aware of. But it seems to me like it defeats the purpose of having a standard in the first place.
On the other hand if I want to write some Perl, I do an emerge perl (substituting your package manager of choice) and then I have Perl. The same exact Perl (barring version differences, and slight differences between Perls on different OSes) that every other Perler in the world has. If I write a Perl script, any other person with Perl could reasonably expect to be able to run it. Same with PHP, Python, Ruby, and many other languages. (People are working on separate interpreters for Ruby, e.g. JRuby but from what I know, they're going to great lengths to make it run EXACTLY like the "official" Ruby, even down to copying bugs to maintain compatibility.)
I know now that there are things like Lisp in a Box that give you an easily-installable Common Lisp environment. Which is really nice for learning purposes. I wish I'd have found it back in 2006 (if it existed at that time). But it only delays the mess you're going to have to handle someday if you want to write portable code, or if you want to use anything other than CLISP or Allegro.
I have an urge to start porting some of my favorite Ruby gems to Common Lisp. But I am not going to install 10 CL implementations just so I can re-write my code 10 times to get it working on all the Lisps in the world. Very likely my code would be ruthlessly SBCL-specific.
(This is one reason I'm kind of hoping Arc takes off. Hopefully there would only be one Arc implementation.)
PAIP, review one of probably many
I got through the first four chapters of PAIP yesterday and today. It's good so far. I had reservations about spending $80 on a book, but it's 950 pages and almost all of it is good stuff, from what I can see at a glance. Little space is wasted on introduction material or tutorials for people who know nothing about programming. He dives right into the good stuff in chapter four.
Already I've picked up a few nice bits of info, interesting standard functions / macros like TRACE which is good for debugging. And SUBLIS which takes a list of (key . value) sublists and replaces all the keys with all the values, in some other list. Common Lisp is such a freaking huge language, in terms of its standard library of functions. Good thing or bad thing? It may be a bad thing if not for the fact that you load Lisp once and it persists forever.
Norvig makes an interesting point about the REPL. What do most programs need to do? Take input from the user, do something with it, and give output back to the user.
To get input, your program can read from command-line args, or prompt to STDIN, or take it out of text fields in a QT app, or read it from a config file. But it's generally always strings. So you get some strings, and then what? You have to figure out what the strings say, and either turn the strings into something usable or use the strings to figure out what action to perform. So you may have a dedicated library to parse command-line args and a dispatch table to map them to function calls. Or you may match the strings against a regex and depending what the strings look like, turn them into your language's representation of an integer or float using some "parseInt" or "parseFloat" functions. Or if your strings are XML, you may parse them into some big funky XML structure, then access bits of that structure to figure out what to do.
In Lisp on the other hand, you happen already to have a powerful reader/parser at your disposal: the REPL itself. It already knows how to read string representations of lists, numbers, pathnames, and many other things, and it can turn them into Lisp objects for you with no effort on your part. If you were using Ruby or Perl, short of evil eval, you wouldn't get anything close to that capability in your program.
What's more, the REPL knows how to read string representations of Lisp code (new functions and function calls etc.), and evaluate the code and end up with Lisp objects. It would be like if you wrote a C program which prompted you for input, and the input you gave could be a string representing a new C program that when run would produce a string that contained your input (or produce in-memory C structures your C program could use). So the main program would call GCC and compile and run your input to get results and use those results as its final input. Except that still wouldn't be nearly as powerful as what Lisp is doing.
So if you can figure out how to shape your input into a form that the Lisp reader can read, you're set. And it so happens sexps are a great representation for a great many things. Maybe this is part of why Lispers seem to worship the REPL and shun compiling their apps into command-line, standalone executables? Maybe this is part of what's meant by the oft-quoted-to-death Greenspun's 10th Rule?
Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.
Much more PAIP to follow. It should be enjoyable. There's nothing like a good book.
False
In Ruby, the only things that are false are nil and false. This is a nice change over other languages where other things evaluate to false in boolean tests. Like C and many others, which considers the integer 0 to be false. So is 0.0 false? How about 1e-999999? Or there's Perl, which also considers an empty string to be false, and auto-casts between numbers and strings, which can make things fun. And of course Perl has Perlish insanity like 0E0, "zero but true".
However all languages pale in comparison with PHP. They provide these handy charts to help you figure out what's true and what's false.
That really sums up PHP well. You have three standard functions (isset, is_null, empty) with completely inconsistent names. Two equality test operators whose meaning completely changes between PHP versions. And then a couple of 12x12 grids of crap necessary to figure out how they behave.
"0" == 0? True.
0 == ""? True.
"0" == ""? False...
null == array()? True....?
Really makes you appreaciate Ruby.
Javascript forays, PHP adventures
To add yet another language to the ever-expanding list of languages I know well enough to get by but have far from mastered, in the past week or two at work I've needed to play with Javascript a lot. I've long thought of Javascript as the bane of my existence, if only for the horrid browser incompatibilities you have to deal with. I hate hate hate hate writing code that needs to have a bunch of silly conditionals that change the code's behavior based on differences in the underlying platform. In this case of course the platform is a browser, but I hate it equally dealing with differences between operating systems, differences between versions of libraries, and other similar things.
The ideal situation is to use some library where someone has already worked out those platform-dependent quirks. In the case of Javascript, there's JQuery. It really does work wonders. I read recently that Google uses it for some of their sites, and I can probably see why. JQuery gives you some helper functions that let you deal very nicely with event handling and element-selection.
To my surprise, Javascript actually is turning out to be a surprisingly powerful language. It apparently supports closures, and functions are first-order objects. The OOP system in Javascript is oddly different from other languages I've used, but it's workable. I'm doing some relatively funky stuff, and Javascript is handling it well enough. And everything I've done in JQuery so far works in IE6 and Firefox. That's a good sign.
In the course of all this, I'm also using PHP. I had to repress a PHP-induced shudder even as I write this. But this is my first venture into the realm of PHP5, and again it's turning out OK. They made a lot of changes between PHP4 and PHP5, many of which made a difference. The language is still far too verbose for my tastes; Ruby has spoiled me in terms of variable names without the need for Perl-style sigils. And the overload of array()'s in PHP still drives me batty. But this time, I'm making heavy use of some third-party libraries, e.g. Smarty templates for my interface, and Spyc to read some YAML for config and input files. It lets me do stuff in PHP without actually writing much code in PHP. Can't beat that.
Maybe that's the secret of life: to make up for deficiencies in languages I dislike by using tons of third-party libraries. Maybe. Well, no, probably not.
In other non-news, my Gentoo box should arrive at my door via UPS on Thursday. Sweet sweet salvation. I'm going to put this Vista laptop in the closet and bolt the door shut.
Redemption, sadly.
Today I rammed smack up against a brick wall in my C# endeavors. The problem at hand was something that seems pretty simple: Randomly shuffle an array. Anyone who knows Ruby knows that this is super easy. Here's one way:
arr.sort_by { rand }
How do you do this in C#? Search google for some tips and view the abject horror of a verbose and non-orthogonal weak language. Read if you dare. Find the shuffle routine in this labyrinth of code, and feel the pain. Have some more if you like. ...cringe. Then read this one. And then read on Wikipedia why this last solution is pretty much totally wrong. And why the Ruby one above is very much right.
So a lot of people wrote a lot of C# code, much of it ugly, to solve this problem, so what? You can find bad code in any language. My point is, to borrow a mantra from the world of Lisp, not all programming languages are created equal. Sure, all programming languages are equally powerful in the Turing-complete sense; you could write a Ruby interpreter in C# and solve the above problem that way. But there are things about some languages that make it much more difficult to solve some problems than it would be in other languages. Passing around blocks in Ruby makes it super easy to solve a problem like randomly shuffling an array. In C#, Java, etc. you'd at the very least have to make some kind of object that represents a sorting algorithm, some kind of method that takes and uses such an object, and you'd also have to worry about all the type compatibility between all of the above. Or I could use some kind of delegate method, which is C#'s version of function pointers, from what little I read. Both of those solutions are a lot more code than I really want to write to solve such a simple problem.
In Perl, to do this kind of thing, you'd take the array, map it into a hash of array-element / random-number pairs, sort by the random numbers, and then re-map it into an array. More verbose than Ruby, but better than nothing. C# has Hashtables and ArrayLists, so why not try it the longhand Perl way? That's what I ended up trying in C# at work. At this point, I ran up against all the lovely limitations of statically typed languages. First I made a hash and tried to get a list of its keys and store them in a native array. You can't do that, because Hashtable returns an ICollection object, not a plain old array, and you can't cast one into the other. C# apparently retains some of the ridiculous Array vs. ArrayList, primitive type vs. object type bullcrap that comes from the world of Java. What reason is there to have a subset of things that are completely incompatible with another subset of nearly identical things? What reason is there to have such a thing built into the very foundation of a language? Java has the excuse (maybe) that they started off with a bunch of crap, and had to try to fix their crap without breaking all the code everyone already wrote using the old crap. What's C#'s excuse for copying this horrendously unwieldy mess?
I eventually got everything sorted out, and it didn't take THAT long, but sure it wasn't pretty. And it was silly, unnecessary work. The solution ended up being 20 or so lines of code to do what Ruby does in 13 characters much more elegantly.
Problem number two today came when I tried to printf a string, and strftime a DateTime. Now, there are what seem to be pretty widely accepted standards on how a formt string for how a printf-style function should look. %d, %s, %f, and so on. Similarly lots of languages have a strftime function that uses pretty standard tags to represent month, day, year, hour, minute, second etc. There are some variations between languages, but the general idea is similar enough that you can pick it up very quickly. In many cases you can drop C strings into Perl or Ruby or whatever, and it'll work just the same.
Well, of course, Microsoft craps all over those pseduo-standards. In C# rather than something like "%0.8d", you get something that looks like "{0,8:#}". Same deal for strings to format dates and times; they look nothing like the strftime I'm familiar with.
Why? Why does MS have to do everything differently than everyone else in the world? The choice of format strings is totally arbitrary, so why not use what every freaking other language in the world uses? Is it to keep MS-only programmers mentally tied into MS-only languages so that anything non-MS looks alien to them? Are the MS-style format strings any more powerful than printf-style? Not that I can tell; the C# ones give you special tags for "currency" and to comma-separated numbers, but couldn't they have just added a "%$" and "%," to the MS version of printf or something? Read this trash. I spent way too much time today reading through this small encyclopedia of documentation on MS-style format strings just to re-learn something I already knew very well.
