tag:blogger.com,1999:blog-86419657888683377402024-03-13T17:21:50.669-03:00O Que Me InteressaGustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.comBlogger126125tag:blogger.com,1999:blog-8641965788868337740.post-80157726754927089282020-11-09T22:26:00.001-03:002020-11-09T22:33:56.629-03:00Perl is Dead ExpressiveA few days ago when I knew that <a href="https://www.infoworld.com/article/3596069/python-soars-past-java-in-tiobe-language-index.html" target="_blank">Python is now officially the new <strike>Cobol</strike> Java</a> I remembered a conversation I had several years ago with some colleagues about programming languages. <a href="https://blog.gnustavo.com/2008/08/timtowtdi.html">I blogged about it in Portuguese then</a> and I think it would be nice to also have it in English.<div><br /></div><div>One of my teammates, an ardent proponent of Python, was presenting some slides boasting the virtues of the language. At some point he started presenting some dangerous slides ... each comparing Python to another programming language. Perl, Bash, Java, and Ruby. I found it dangerous because I think that comparing two languages decently is a complex task that cannot be condensed into one slide. First, a set of objective criteria for comparison must be defined. Then, it is necessary to take into account the context in which the language is being used. Things like the domain of the applications that will be developed, the development and deployment platforms, the developers' experience with the language, the size of the team, and the time constraints of the project. After all that, you have to resist the temptation to argue passionately for the language of your preference in order to give at least the appearance of rationality.<div><br /></div><div>But that's ok ... in a small group, this type of discussion is as stimulating and harmless as talking about politics, sports, or religion. ;)</div><div><br /></div><div>I think it was on the Bash slide that he suggested a problem for which a standard Unix shell would not offer a solution as economical and as readable as the Python interactive shell could. The problem was, more or less, the following. Suppose there is a set of files in a directory which names consist of an alphabetical prefix, followed by a sequence of digits and ending in the extension .jpg. For example:
<pre class="shell"> $ ls
a0.jpg b1.jpg c123.jpg
</pre>
The challenge is to rename them so that all filenames have the same number of digits in them. In the case above, the result should be:
<pre class="shell"> a000.jpg b001.jpg c123.jpg
</pre>
I left the talk with the problem in my head and the first thing I did was to come up with some one-liners:
<pre class="shell"> # printing the names
$ ls | perl -lpe \
's/^([a-z]+)(\d+)\.jpg/sprintf "%s%03d.jpg", $1, $2/e'</pre><pre class="shell"> a000.jpg
b001.jpg
c123.jpg
# generating commands to rename them
$ ls | perl -lpe \
's/^([a-z]+)(\d+)\.jpg/sprintf "mv -n %s %s%03d.jpg", $&, $1, $2/e'</pre><pre class="shell"> mv -n a0.jpg a000.jpg
mv -n b1.jpg b001.jpg
mv -n c123.jpg c123.jpg
# executing commands in the shell
$ ls | perl -lpe \
's/^([a-z]+)(\d+)\.jpg/sprintf "mv %s %s%03d.jpg", $&, $1, $2/e' \
| sh
$ ls
a000.jpg b001.jpg c123.jpg
</pre>
That's how I usually develop a shell solution. Instead of loops I prefer to use commands to generate other commands, like the mv above, so that I can easily verify that I am doing the right thing. After making sure of that, just add a "| sh " at the end of the pipeline to execute the generated commands.
Perl has some very useful options for making one-liners like this. -l, -a, -n, -p, and -e are the ones I use most often. Read the <a href="https://perldoc.perl.org/perlrun">perlrun</a> documentation to learn more about them and many other interesting options.
But, not to say that Perl can't do things alone, I added a solution that doesn't use the shell at the end.
<pre class="shell"> # doing everything in Perl
$ ls | perl -lne \
'if (/^([a-z]+)(\d+)\.jpg/) {
rename $_, sprintf "%s%03d.jpg", $1, $2
}'
$ ls
a000.jpg b001.jpg c123.jpg
</pre>
Another teammate, who is a Bash fan, didn't let it go and came up with the following solutions:
<pre class="shell"> $ ls
a0.jpg b1.jpg c123.jpg
$ for i in *.jpg; do
> j=${i%*.jpg}
> printf "mv -n %s %s%03d.jpg\n" $i ${j//[0-9]/} ${j//[a-z]/}
> done</pre><pre class="shell"> mv -n a0.jpg a000.jpg
mv -n b1.jpg b001.jpg
mv -n c123.jpg c123.jpg
$ for i in *.jpg; do
> j=${i%*.jpg}
> printf "mv -n %s %s%03d.jpg\n" $i ${j//[0-9]/} ${j//[a-z]/}
> done | sh
$ ls
a000.jpg b001.jpg c123.jpg
</pre>
Ninja! I'll confess that I never had the willpower to learn these advanced bash string manipulation strokes. For me, shell is a glue that serves to stick other commands together. Whenever I need something more complicated, like data structures or regular expressions, I don't think twice about using Perl.
But the Python die-hard counter attacked with this:
<pre class="shell">$ python
Python 2.7.18 (default, Aug 4 2020, 11:16:42)
[GCC 9.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> for name in os.listdir("."):
... base, number, ext = name[0], name[1:name.find(".")], name.split(".")[1]
... os.rename(name, "%s%03d.%s"%(base, int(number), ext))
...
>>>
$ ls
a000.jpg b001.jpg c123.jpg
$ # readability counts
</pre>
Ah ... what a subtle criticism in that last comment.</div><div><br /></div><div>IS IT?</div><div><br /></div><div>But <a href="http://www.paulgraham.com/power.html">Succinctness is Power</a>!</div><div><br /></div><div>When I want to solve a problem with a one liner "readability" is irrelevant, because if I am not going to save the solution in a script, no one else will read it, right? Come on ... if I was going to save it in a script I could write something more like his Python version. Something like this:
<pre class="perl"> opendir CWD, '.';
foreach $name (readdir CWD) {
if (($base, $number, $ext) = ($name =~ /^(.)(\d+)\.(.*)/)) {
rename $name, sprintf("%s%03d.%s", $base, $number, $ext);
}
}
closedir CWD;
</pre>
Hmmm ... I didn't even try to break the name with string operations because I find the regular expression more direct and, in this case, more readable. To get even more readable I would replace the commands opendir, readdir, and closedir by a glob pattern:
<pre class="perl"> foreach $name (<*.jpg>) {
if (($base, $number, $ext) = ($name =~ /^(.)(\d+)\.(.*)/)) {
rename $name, sprintf("%s%03d.%s", $base, $number, $ext);
}
}
</pre>
Better, right?
But it’s still not good. It's very, how can I say it... heavyweight. One of the big differences between Perl and many other languages, Python in particular, is that we don't always have to be explicit. It is more or less like using pronouns or hidden subjects in natural languages. When you learn a foreign language at first you don't know it very well and baby-talk like this:
<blockquote>
Joe is married. Joe has five children. Joe's children are all single.
</blockquote>
Then you learn to use the pronouns and start speaking more economically.
<blockquote>
Joe is married. He has five children. They are all single.
</blockquote>
Until you are really fluent in the language and speak naturally like this:
<blockquote>
Joe is married and has five children, all single.
</blockquote>
Unintelligible? Of course not. Unless you are just starting to learn English. We usually talk to people who are as fluent as we are, so we can, and should, be economical and direct. By avoiding redundancies we are not just more direct. We are also more intelligible (or readable), because we do not insert in the speech that series of repeated names that end up polluting the text, hiding the real content of the message.
Well, all of this is to explain my next version, in which I delete the variable $name, because in Perl the loop iterator may be implicitly operated on, like this:
<pre class="perl"> foreach (<*.jpg>) {
if (($base, $number, $ext) = /^(.)(\d+)\.(.*)/) {
rename $_, sprintf("%s%03d.%s", $base, $number, $ext);
}
}
</pre>
If you don't know Perl you won't know that the regular expression is being applied to the foreach implicit iterator. But if you've never seen Perl, that's not your biggest problem, is it? Oh, and $_ is the "pronoun" we use to refer explicitly to the iterator inside the loop.</div><div><br /></div><div>On second thought, these local variables are not serving much purpose other than naming the parts captured by the regular expression. If we were to use them often, it would be proper. But to only use them once on the next line? The regular expression is clear enough (after gaining some experience with them, obviously). How about getting rid of those variables?
<pre class="perl"> foreach (<*.jpg>) {
if (/^(.)(\d+)\.(.*)/) {
rename $_, sprintf("%s%03d.%s", $1, $2, $3);
}
}
</pre><div>I could use <a href="https://perldoc.perl.org/perlre#Capture-groups">named capture groups</a> to use names instead of numbers to refer to the captures. But in a small block like this I usually don't bother.</div><div><br /></div>
Still ... it's looking too much like C to me. In Perl it's more direct and readable to interpolate the variables in the format string:
<pre class="perl"> foreach (<*.jpg>) {
if (/^(.)(\d+)\.(.*)/) {
rename $_, sprintf("$1%03d.$3", $2);
}
}
</pre>
Hmmm ... the important thing is the rename ... the if is an accessory. In Perl, we can reverse the test and the action, more or less like when we choose the active voice or the passive voice for stylistic reasons. So, let's put what matters first:
<pre class="perl"> foreach (<*.jpg>) {
rename $_, sprintf("$1%03d.$3", $2)
if /^(.)(\d+)\.(.*)/;
}
</pre>
Nice. And we saved a pair of braces too, see?</div><div><br /></div><div>Ah ... being so succinct it becomes easier to perceive the opportunity to make trivial optimizations:
<pre class="perl"> foreach (<*.jpg>) {
rename $_, sprintf("$1%03d.jpg", $2)
if /^(.)(\d+)\.jpg$/;
}
</pre>
Or timely generalizations:
<pre class="perl"> foreach (<*.jpg>) {
rename $_, sprintf("$1%03d.jpg", $2)
if /^([a-z]+)(\d+)\.jpg$/i;
}
</pre>
It seems very readable for me. How about you?</div><div><br /></div><div>Anyway, at least it proves that <a href="http://en.wikipedia.org/wiki/TIMTOWTDI">There Is More Than One Way To Do It</a>.</div><div><br /></div><div><b>Addendum</b>: Sometime after writing this I discovered the <a href="https://metacpan.org/pod/distribution/File-Rename/rename.PL">rename</a> command. With it the solution is trivial:
<pre class="shell"> $ rename 's/(\d+)/sprintf("%03d", $1)/e' *.jpg
</pre>
Ah ... rename is written in Perl. :-)
</div></div>Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com3tag:blogger.com,1999:blog-8641965788868337740.post-78174949311758892692019-04-22T21:31:00.001-03:002019-04-23T21:36:49.319-03:00Perl Weekly Challenge 005<a href="https://perlweeklychallenge.org/blog/perl-weekly-challenge-005/">This week's challenges</a> are all about <a href="https://en.wikipedia.org/wiki/Anagram">anagrams</a>.<br />
<br />
The first one is to<br />
<blockquote class="tr_bq">
Write a program which prints out all anagrams for a given word. For more information about Anagram, please check this <a href="https://en.wikipedia.org/wiki/Anagram">wikipedia</a> page.</blockquote>
It's not said but I assume that, besides the word, the program must also read a dictionary of words in which it will look for anagrams. My solution is simple and very much alike the solution to last week's second challenge.<br />
<br />
The ideia is to use a hash function that generates a key for each word so that anagrams always produce the same key and non-anagrams always lead to different keys. The hash function I use lowercases the word so that we compare letters case insensitively. Then it splits the word in all of its letters, sorts, and joins them together. So, for example, "Perl" is keyed as "elpr".<br />
<br />
The script first generates the key for the input word. Then it iterates for all dictionary words, printing those that have a key equal to the input word's key.<br />
<br />
<script src="https://gist.github.com/gnustavo/889df80434c6826720ed37f4491b7d8e.js"></script><br />
The second challenge is to<br />
<blockquote class="tr_bq">
Write a program to find the sequence of characters that has the most anagrams.</blockquote>
My solution first reads all of the dictionary words and classify them in anagrams using the same hash function of the first script. Then it finds and prints the keys associated with the maximum number of anagrams.<br />
<br />
<script src="https://gist.github.com/gnustavo/1ca63ef030fcb34a221bf2a6b778b386.js"></script>
<br />
And this is how they work. First I use the second script to grok the sequence of characters that has the most anagrams in my Ubuntu dictionary. Then I use the first script to grok all the anagrams associated with it:<br />
<script src="https://gist.github.com/gnustavo/e17ee93cd84681a9abeb3cacc31cfa31.js"></script><br />
-----<br />
I came up with another solution to the second challenge that is shorter, faster and uses no modules:<br />
<br />
<script src="https://gist.github.com/gnustavo/03f27022e41b414140d2162ebc2dbd9c.js"></script>Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com0tag:blogger.com,1999:blog-8641965788868337740.post-23293305096005804242019-04-17T22:19:00.001-03:002019-04-21T16:32:19.431-03:00Perl Weekly Challenge 004This week I submitted my solutions via a <a href="https://github.com/manwar/perlweeklychallenge-club/pull/71">pull request</a> to the GitHub's repository.<br />
<br />
This was the first time I solved the first problem, because it was interesting:<br />
<blockquote class="tr_bq">
Write a script to output the same number of PI digits as the size of your script. Say, if your script size is 10, it should print 3.141592653.</blockquote>
After seeing a few solutions by other people I feel that <a href="https://github.com/manwar/perlweeklychallenge-club/blob/0c48a02a29732da2aa1aef4ff1dd124665f3cd62/challenge-004/gustavo-chaves/perl5/ch-1.pl">my solution</a> is a little dumb. I wrote the smallest script I could write, saw its size and edited back the number of characters I wanted. Some other solutions use clever ways to grok the scripts size dynamically.<br />
<br />
The second problem was interesting too:<br />
<blockquote class="tr_bq">
You are given a file containing a list of words (case insensitive 1 word per line) and a list of letters. Print each word from the file than can be made using only letters from the list. You can use each letter only once (though there can be duplicates and you can use each of them once), you don’t have to use all the letters. (Disclaimer: The challenge was proposed by Scimon Proctor)</blockquote>
<a href="https://github.com/manwar/perlweeklychallenge-club/blob/0c48a02a29732da2aa1aef4ff1dd124665f3cd62/challenge-004/gustavo-chaves/perl5/ch-2.pl">My solution</a> is similar to others I saw after having written it. It's not particularly clever, but I find it very readable. This is how it works in my Linux box:<br />
<br />
<span style="background-color: white; color: blue; font-family: "courier new" , "courier" , monospace;"><b>$ ./ch-2.pl /usr/share/dict/words Perl</b></span><br />
<span style="background-color: white; color: blue; font-family: "courier new" , "courier" , monospace;"><b>E</b></span><br />
<span style="background-color: white; color: blue; font-family: "courier new" , "courier" , monospace;"><b>L</b></span><br />
<span style="background-color: white; color: blue; font-family: "courier new" , "courier" , monospace;"><b>Le</b></span><br />
<span style="background-color: white; color: blue; font-family: "courier new" , "courier" , monospace;"><b>P</b></span><br />
<span style="background-color: white; color: blue; font-family: "courier new" , "courier" , monospace;"><b>Perl</b></span><br />
<span style="background-color: white; color: blue; font-family: "courier new" , "courier" , monospace;"><b>R</b></span><br />
<span style="background-color: white; color: blue; font-family: "courier new" , "courier" , monospace;"><b>e</b></span><br />
<span style="background-color: white; color: blue; font-family: "courier new" , "courier" , monospace;"><b>l</b></span><br />
<span style="background-color: white; color: blue; font-family: "courier new" , "courier" , monospace;"><b>p</b></span><br />
<span style="background-color: white; color: blue; font-family: "courier new" , "courier" , monospace;"><b>per</b></span><br />
<span style="background-color: white; color: blue; font-family: "courier new" , "courier" , monospace;"><b>r</b></span><br />
<span style="background-color: white; color: blue; font-family: "courier new" , "courier" , monospace;"><b>re</b></span><br />
<span style="background-color: white; color: blue; font-family: "courier new" , "courier" , monospace;"><b>rep</b></span><br />
<div>
<br /></div>
<div>
That's it for this week.<br />
<br />
----<br />
After a while I came up with a <a href="https://github.com/manwar/perlweeklychallenge-club/pull/83/files#diff-d22154ec60122aab86a236d6fc0ace72">new solution to the second problem</a> which is more concise because it's written in a more functional style. But it depends on the List::Util module.</div>
<div>
<br /></div>
Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com0tag:blogger.com,1999:blog-8641965788868337740.post-43132762631308712352019-04-11T22:42:00.002-03:002019-04-14T16:08:06.156-03:00svndumpsanitizer is a gemI've been supporting Subversion repositories in my work for more than ten years already. During this time I've grudgingly done my fair share of migrations, moving partial histories from one repository to another.<br />
<br />
The standard procedure consists in dumping the source repository, filtering the resulting dump to keep only the part of the history you're interested in, and loading the resulting dump into the target repository. It's possible to do it in a single pipeline like this:<br />
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">svnadmin dump <i>source</i> | svndumpfilter <i>options</i> | svnadmin load <i>target</i></span></blockquote>
If you ever did this to any non-trivial repository you must know how exasperating it can be to come up with the correct options. It's a trial-and-error process because you never know exactly which paths you need to include in the filter, since Subversion histories have a tendency of containing all sorts of weird movements and renamings, which break the filtering. Then, you have to understand which path you have to add to the filter and restart the process from the beginning.<br />
<br />
This week I embarked in a Subversion migration adventure. If I only knew how I would regret it... I had to move the histories of some 15 directories from three source repositories into a sub-directory of a single target repository. They are big and old repositories, but the directories seemed innocent enough that I started very confident. To be sure, all but two of the directories were moved easily.<br />
<br />
The remaining two kept me busy for most of the week though. Their histories are long and windy. During the course of my trials I became aware of some options in newer versions of the "svnadmin dump" command that promised to make it possible to avoid the intermediary svndumpfilter command. But it failed. Hard. Repeatedly. Annoyingly.<br />
<br />
I gave myself today as my last chance to finish the process. I almost gave up but by chance I stumbled upon a link to <a href="https://miria.homelinuxserver.org/svndumpsanitizer/">svndumpsanitizer</a>... and I was saved.<br />
<br />
It's a simple, fast, and intelligent tool that seems to solve all the problems that the svndumpfilter program has. And it's superbly documented too. It's page explains very well the usual problems we get with svndumpfilter and how it overcomes them.<br />
<br />
Discounting the time to make the initial dump and the final load, the filtering took less than a minute. Awesome!<br />
<br />
Kudos to svndumpsanitizer's author, <a href="https://github.com/dsuni">dsuni</a> at GitHub, for such a gem!<br />
<br />Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com0tag:blogger.com,1999:blog-8641965788868337740.post-4091565576942714502019-04-07T23:24:00.000-03:002019-04-14T16:08:06.149-03:00Perl Weekly Challenge #3<a href="https://perlweeklychallenge.org/blog/perl-weekly-challenge-003/">This week's challenge</a> is to:<br />
<br />
<i>Create a script that generates Pascal Triangle. Accept number of rows from the command line. The Pascal Triangle should have at least 3 rows. For more information about Pascal Triangle, check this <a href="https://en.wikipedia.org/wiki/Pascal%27s_triangle">wikipedia</a> page.
</i><br />
<div>
<br /></div>
<div>
I don't know why there is a restriction in the number of rows. Here's my quick&dirty answer:<br />
<br />
<script src="https://gist.github.com/gnustavo/8c03a0cc6dca22f074526fb378aa4f3e.js"></script>
<br />
Here's how to use it:<br />
<br />
<script src="https://gist.github.com/gnustavo/5d32c8681158f6190514b46fbd8239d5.js"></script>
</div>
Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com0tag:blogger.com,1999:blog-8641965788868337740.post-74892180057530696922019-04-05T20:55:00.001-03:002019-04-05T20:55:56.831-03:00O princípio e o fimMeu filho está resfriado e começou a discutir com minha esposa sobre que remédio ele deveria tomar para dor e febre.<br />
<br />
Eu não estava prestando muita atenção, mas percebi que estavam discutindo sobre as diferenças entre os princípios ativos. Ela argumentava que se os remédios tinham princípios ativos distintos não tinha problema tomar dois de uma vez, ao passo que ele teimava que se ambos serviam para a mesma coisa isso não fazia muito sentido... Devia ser mais complicado do que isso, mas, como eu disse, eu não estava prestando atenção.<br />
<br />
Tentando ajudar eu perguntei:<br />
<br />
- O que importa se eles não têm o mesmo princípio se ambos têm o mesmo fim?<br />
<br />
Não ajudou em nada... Mas não ficou bonito? ;-)<br />
<br />
<br />Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com0tag:blogger.com,1999:blog-8641965788868337740.post-33269490929987566032019-03-31T23:35:00.000-03:002019-04-14T16:08:53.443-03:00Perl Weekly Chalenge #2<div class="tr_bq">
Last week I sent my solution to the <a href="https://perlweeklychallenge.org/blog/a-new-week-a-new-challenge/">Perl Weekly Chalenge #1</a> via email. It was fun and simple.</div>
<br />
<a href="https://perlweeklychallenge.org/blog/perl-weekly-challenge-002/">This week's challenge</a> is to "write a script that can convert numbers to and from a base35 representation, using the characters 0-9 and A-Y."<br />
<br />
I cannot do it as a one-liner this time, but it was still fun. While trying to solve it I realized that it wouldn't be much harder to implement a general solution to convert from any base to any base between 2 and 36.<br />
<br />
This is my solution:<br />
<br />
<script src="https://gist.github.com/gnustavo/f6e044a4c6baaffc4174c15334fb49df.js"></script>
And this is how it works:<br />
<br />
<script src="https://gist.github.com/gnustavo/127c7bd59e4cff94ccaa709f227d27bb.js"></script>
<div>
<br /></div>
Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com0tag:blogger.com,1999:blog-8641965788868337740.post-28461755662775054842019-02-09T19:51:00.001-02:002019-02-09T19:51:22.115-02:00De nove a dezAnteontem eu estava conversando com meu filho Tiago (que já está com 20 anos!) quando tive um lampejo de genialidade e fiz um comentário inteligentíssimo e muito engraçado. Não me lembro mais exatamente o que era, mas eu achei tão bom que perguntei pra ele empolgado:<br />
<br />
- Filhão, quanto você dá pra mim?<br />
- Por que, pai?<br />
- Pelo comentário inteligente que eu acabei de fazer.<br />
- De quanto a quanto?<br />
- De zero a dez.<br />
- Zero!<br />
<br />
- Como assim, filhão? O comentário foi ótimo, pô!<br />
<br />
Ele viu que eu fiquei muito frustrado e me aconselhou:<br />
<br />
- Pai, quando você quiser uma nota boa você não pode dar muita liberdade pra quem vai dar a nota.<br />
- Como assim?<br />
- Não pode pedir a nota de zero a dez.<br />
- Ah, não?<br />
- Não. Tem que pedir de nove a dez, por exemplo.<br />
- Hmm... tá, de nove a dez quanto você me dá?<br />
- Nove!<br />
<br />
Tá aí. Fiquei bem mais feliz. :-)<br />
<br />
<br />Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com0tag:blogger.com,1999:blog-8641965788868337740.post-14772275546495476572019-01-01T16:34:00.000-02:002019-01-01T16:34:11.925-02:00Juliana dá o trocoHá 11 anos minha filha Juliana sofria na mão do irmão Tiago e da prima Ana Flávia que, mais velhos, <a href="https://blog.gnustavo.com/2007/06/saber-perder.html">não lhe davam chance durante as brincadeiras</a>.<br />
<br />
Ontem ela foi à forra: fizemos uma dupla, eu e ela, e jogamos truco contra o irmão e a prima. Ganhamos duas partidas... a primeira de 12 a 0!<br />
<br />
Aprendeu a ganhar, hein, Ju? 😁Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com0tag:blogger.com,1999:blog-8641965788868337740.post-64885024289075937772018-12-27T23:18:00.000-02:002018-12-27T23:18:19.566-02:00Filhão, olha aqui que você vai gostar!Olá... há quanto tempo, não?<br />
<br />
Pois é... eu ainda estou por aqui. Semana passada meus filhos (que já estão bem grandes) encontraram esse blog por acaso e demos muitas risadas relendo algumas das histórias deles que eu registrei aqui.<br />
<br />
Deu uma saudade danada d'eles pequenos...<br />
<br />
Lembramos de algumas histórias mais recentes que eu devia ter registrado. Essa aqui aconteceu no ano passado, eu acho:<br />
<br />
Estávamos os quatro na cozinha, jantando. Estávamos comendo alguma comida oriental com carne e bastante molho à base de shoyu.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-RTF0EA4Ybe4/XCV5tEGSURI/AAAAAAABEVU/PmRfsz0oW-UCF2asE8V9swOtNMYLKnbfwCLcBGAs/s1600/20171210_201336.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://4.bp.blogspot.com/-RTF0EA4Ybe4/XCV5tEGSURI/AAAAAAABEVU/PmRfsz0oW-UCF2asE8V9swOtNMYLKnbfwCLcBGAs/s320/20171210_201336.jpg" width="320" /></a></div>
<br />
<br />
Acho que todos eles já tinham terminado e eu me levantei para preparar um último prato... O problema é que tinha restado muito molho mas pouca coisa sólida na panela. O que eu queria mesmo era aproveitar aqueles sólidos e misturá-los ao arroz que eu já tinha colocado no prato. Mas como é que eu iria pescá-los? Comecei com uma colher, mas logo vi que ia demorar muito, porque eram muitos e pequenos...<br />
<br />
Foi aí que me deu um estalo (ou uma ideia de jerico, segundo minha esposa): uma peneira, é claro!<br />
<br />
Fui até a despensa e peguei uma peneira grande. Coloquei meu prato na pia, com uma mão segurei a peneira sobre ele e com a outra peguei a panela.... Mas eu estava muito orgulhoso da minha ideia e achei que ela merecia plateia... Virei pro meu filho que estava lavando o prato na pia e lhe disse:<br />
<br />
- Filhão, olha aqui que você vai gostar!<br />
<br />
E despejei todo o conteúdo da panela na peneira.<br />
<br />
...<br />
<br />
Não sei se eu percebi sozinho a burrada que eu fiz ou se foi a cara de interrogação do meu filho que me deu o sinal. Mas o resultado foi o inverso do que eu pretendia: meu prato virou uma sopa de arroz com shoyu e todos os sólidos que eu queria ficaram na peneira. :-(<br />
<div>
<br /></div>
Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com0tag:blogger.com,1999:blog-8641965788868337740.post-11477012829764311142015-12-20T10:19:00.000-02:002019-04-14T16:08:53.429-03:00Tracing Subversion path historiesDuring the past few days I am trying to convert a large Subversion repository to Git. Every time I do this for a big repo with a large history I face the problem of mapping branches and tags from their path-based locations on Subversion to their reference-based names in Git.<br />
<br />
The problem is that since branches and tags in Subversion are represented as simple directories with naming conventions, and conventions tend to change, for a large history it's very common to see that branches and tags may have been moved around.<br />
<br />
Every conversion tool I know require that we specify the paths of Subversion trunk, branches, and tags and how they are to be mapped as Git references. I've used <a href="https://git-scm.com/docs/git-svn">git-svn</a> in the past but now I'm experimenting with <a href="http://www.subgit.com/">subgit</a> to see how it goes. So far so good. But the mapping problem still requires some thought and experimentation.<br />
<br />
For example, in the repo I'm converting different kinds of branches are kept under different directories: feature branches under <span style="font-family: Courier New, Courier, monospace;">/branches/dev</span> and release branches under <span style="font-family: Courier New, Courier, monospace;">/branches/fix</span>. So, I started out with this configuration:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"> branches = branches/dev/*:refs/heads/dev/*</span><br />
<span style="font-family: Courier New, Courier, monospace;"> branches = branches/fix/*:refs/heads/fix/*</span><br />
<br />
However, after having converted everything I noticed that some of the branches had short histories. That is, "git log branchname" showed me just a few commits and ended in an orphan commit. The initial history of the branch, showing when it branched from trunk, was lost.<br />
<br />
With the help of "<span style="font-family: inherit;">svn log"</span> I was able to see that these branches had been moved in the past from <span style="font-family: Courier New, Courier, monospace;">branches/dev</span> to <span style="font-family: Courier New, Courier, monospace;">branches/old</span> and back again. Since I hadn't configured the path <span style="font-family: Courier New, Courier, monospace;">branches/old</span> to be converted, all that history was lost. So, I had to insert another line to the configuration file and start again:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"> branches = branches/old/*:refs/heads/old/*</span><br />
<br />
Mind you, this is a simplified version of the story, as there was many more instances of branch and tag renaming that I was learning along the way. And doing that using "svn log" commands isn't fun.<br />
<br />
So, I looked for some tool that could help me figure out at once and for all branches and tags I'm interested in their complete naming history. Basically I'd like to specify a list of paths existing at HEAD and the tool should tell me the complete history of renames for each of them, since they were copied originally from trunk or other branch.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://upload.wikimedia.org/wikipedia/commons/thumb/7/7f/Bidirectional_scattering_distribution_function.svg/2000px-Bidirectional_scattering_distribution_function.svg.png" imageanchor="1"><img border="0" height="320" src="https://upload.wikimedia.org/wikipedia/commons/thumb/7/7f/Bidirectional_scattering_distribution_function.svg/2000px-Bidirectional_scattering_distribution_function.svg.png" width="285" /></a></div>
<br />
I wasn't able to find anything like this so I ended up writing <a href="https://github.com/gnustavo/bag/blob/master/svn-trace-paths.pl">a small script that's really useful</a>.<br />
<br />
The script is invoked like this:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"> svn-trace-paths.pl --pathsfile FILE --logfile FILE >trace.csv</span><br />
<br />
The --pathsfile argument must be a file listing one path per line. Each path represents a branch, a tag, or trunk, i.e., everything you want to trace the history of.<br />
<br />
the --logfile argument must be a file containing the complete log of the repository in XML format, which you may produce like this:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"> svn log --xml -v REPO_ROOT_URL >logfile.xml</span><br />
<br />
Generating the complete log for a repository may take minutes or even hours depending on its size and if you're accessing it locally or from a remote server. So, it's better to produce it once to be able to reuse it multiple times after if you want to trace histories for different paths.<br />
<br />
The script produces a CSV spreadsheet on its standard output, so it's better to redirect it to a file. Each line represents the creation of a path. The first column has the path and the second column the numeric revision when it was created. If it was created as a copy from another path (which is the most common situation if you're following branches and tags) it has two more columns showing the name of the original path and the revision from which is was copied. A fictional example may make it clearer:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"> /tags/1.0.1 1534 /branches/fix/1.0 1533</span><br />
<span style="font-family: Courier New, Courier, monospace;"> /tags/1.0.0 1234 /branches/fix/1.0 1230</span><br />
<span style="font-family: Courier New, Courier, monospace;"> /branches/fix/1.0 999 /trunk 998</span><br />
<span style="font-family: Courier New, Courier, monospace;"> /trunk 1</span><br />
<br />
In this example you could have passed in --pathsfile only the two paths for the tags (<span style="font-family: Courier New, Courier, monospace;">/tags/1.0.1</span> and <span style="font-family: Courier New, Courier, monospace;">/tags/1.0.0</span>). The script would start out tracking them and would find along the way from which paths they were copied, at which point it would start tracking those. That's why it ended up showing the history of <span style="font-family: Courier New, Courier, monospace;">/branches/fix/1.0</span> and <span style="font-family: Courier New, Courier, monospace;">/trunk</span>.<br />
<br />
Note that since <span style="font-family: Courier New, Courier, monospace;">/trunk</span> wasn't copied from anywhere, it's line shows just the two first columns.<br />
<br />
Also note that <span style="font-family: Courier New, Courier, monospace;">/tags/1.0.0</span> was created on r1234 as a copy from <span style="font-family: Courier New, Courier, monospace;">/branches/fix/1.0</span>, but from a previous revision: r1230. This is common in some situations. If you use an automated continuous integration system to validate your branch and tag them automatically it can take several minutes to validate it while new commits may be created so that at the end the tag must be made to the revision that was validated, which isn't HEAD anymore.<br />
<br />
The repo I'm converting has 235973 revisions and its complete XML log has 534MiB. I'm interested in tracing the history of 287 paths. To process that amount of information the script took 3min29s in my laptop and produced a spreadsheet with 442 lines. Not bad at all, I'd say.<br />
<br />
You can open the resulting spreadsheet and study it to see all the paths that you must configure to be able to convert the complete history of the paths you're interested in.<br />
<br />
With a little shell script you can produce a list of all of them:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"> (csvtool col 1 trace.csv; csvtool col 3 trace.csv) | sort -u</span><br />
<br />
The <a href="http://manpages.ubuntu.com/manpages/wily/man1/csvtool.1.html">csvtool</a> is a really handy command I use here to take only the first and third columns from the CSV file, the ones containing the original and copied paths. I concatenate them all and feed them to "sort -u" which sorts them and remove duplicates.<br />
<br />
That's how I know every path I should consider when I'm configuring my conversion tool.<br />
<br />
Now that I have this script, I'm sure I'll use it whenever I want to know the history of particular long lived branches. It's nice when you do something that ends up having unintended usefulness. :-)<br />
<div>
<br /></div>
<br />Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com0tag:blogger.com,1999:blog-8641965788868337740.post-71788639522064654872013-07-27T14:33:00.000-03:002019-04-14T16:08:53.477-03:00Git Hooks are awesome, but hard<a href="http://www.goodwinandgoodwin.com/_ui/images/sml/hooks.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="133" src="http://www.goodwinandgoodwin.com/_ui/images/sml/hooks.jpg" width="200" /></a>Git is awesome and has profoundly changed the daily work of many developers. In addition to providing a very rich set of concepts and tools, Git is extensible in several ways, which makes it even more powerful.<br />
<br />
One of the ways in which Git's functionality can be extend is via hooks. A <a href="http://git-scm.com/book/en/Customizing-Git-Git-Hooks">Git hook</a> is an external program (usually, a script) that Git invokes during the execution of some of its native operations. As of Git 1.8.3.4 there are 20 different Git hooks. During a <span style="font-family: "Courier New",Courier,monospace;">git commit</span>, for example, Git may invoke up to five hooks in specific phases of the command execution, in this order:<span style="font-family: inherit;"> pre-commit, prepare-commit-msg, commit-msg, post-commit, and post-rewrite.</span><br />
<br />
When Git is about to invoke a hook it looks up for an executable file named after the hook in the repository's <span style="font-family: "Courier New",Courier,monospace;">.git/hooks</span> directory. So, for example, when it's about to invoke the pre-commit hook it looks for an executable file called <span style="font-family: "Courier New",Courier,monospace;">.git/hooks/pre-commit</span> in the repository. If it doesn't find any, it simply continues the command execution. Otherwise, it invokes the file passing to it some information about the current command's state and waits for it to terminate.<br />
<br />
Some hooks can affect the Git command with their exit values. The pre-commit hook, for example, can abort the commit if it terminates abnormally, i.e., with an exit value different from zero. The post-commit hook, however, can't, since it is invoked after the commit has been completed.<br />
<br />
Git passes information about the current state of the command that is being executed to its hooks . This information may go via command parameters, environment variables, or standard input. Each hook has a specific set of information that's passed to it in some specific form.<br />
<br />
By default, when you <span style="font-family: "Courier New",Courier,monospace;">git init</span> or <span style="font-family: "Courier New",Courier,monospace;">git clone</span> a repository the <span style="font-family: "Courier New",Courier,monospace;">.git/hooks</span> directory ends up with some template files, all having the <span style="font-family: "Courier New",Courier,monospace;">.sample</span> suffix in their names and helpful instructions inside explaining how to convert them into working hooks. To enable them, you simply have to edit and to rename them, dropping the suffix. And don't forget to make them executable.<br />
<br />
Run <a href="https://www.kernel.org/pub/software/scm/git/docs/githooks.html"><span style="font-family: "Courier New",Courier,monospace;">git help githooks</span></a> to read details about all the hooks and to understand their most common uses.<br />
<br />
<h3>
What Can Hooks Do?</h3>
<br />
In theory they can do anything allowed by the privileges of the user invoking them. Note that some hooks are invoked by your local Git, such as the above mentioned commit hooks. These hooks run as yourself and have all the privileges that you have to investigate or change your local repository. Other hooks are invoked by the remote Git, the most common being the pre-receive and the update hooks. Those are invoked by the Git process running in the remote repository and are commonly used to reject pushes with commits that don't obey some of the project's agreed upon policies.<br />
<br />
<h3>
Why Are They Awesome?</h3>
<br />
Because they can extend or restrict the functionality of Git's native commands in very useful ways.<br />
<br />
For example, suppose your project's team decide to adhere to a set of coding standards. You could implement a pre-receive hook to run on the central Git server to check those standards in every added or modified source file in every commit, rejecting pushes carrying commits violating those standards. The remote hook's error messages are shown to the users performing the <span style="font-family: "Courier New",Courier,monospace;">git push</span>, letting them know what is wrong with their commits. This way you can automate a significant part of your coding review process.<br />
<br />
Even better, the same hook, slightly modified, could be installed by all developers on their own cloned repositories as a pre-commit or a post-commit hook, letting them know at commit time if they have violated any rule, before going on with development.<br />
<br />
Most hooks are used to check for policy violations such as these. But you can also use them as a notification service. For instance, the post-receive hook is invoked after a successful <span style="font-family: "Courier New",Courier,monospace;">git push</span> and can be used to notify interested parties about recent activity in the central repository.<br />
<br />
You can even use a hook to trigger the execution of some action external to Git, turning it into your Personal Workflow Automatizator <a href="http://pt.wikipedia.org/wiki/Organiza%C3%A7%C3%B5es_Tabajara">Tabajara<span style="font-size: small;">™</span><span style="font-size: large;"><span style="background-color: white; color: black; display: inline ! important; float: none; font-family: sans-serif; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 19.2px; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px;"><span class="Apple-converted-space"></span></span></span></a>. For example, a post-receive hook could check if a specific branch called <span style="font-family: "Courier New",Courier,monospace;">production</span> has been changed and update the system in the production server via <span style="font-family: "Courier New",Courier,monospace;">ssh</span>, <span style="font-family: "Courier New",Courier,monospace;">rsync</span>, or even <span style="font-family: "Courier New",Courier,monospace;">git pull</span> in another clone.<br />
<br />
If your own imagination fails you, you can resort to Google to look for all sorts of useful hook scripts available elsewhere (e.g. <a href="https://github.com/gitster/git/tree/master/contrib/hooks">https://github.com/gitster/git/tree/master/contrib/hooks</a> and <a href="http://google.com/search?q=git+hooks">http://google.com/search?q=git+hooks</a>).<br />
<br />
<h3>
Why Are They Hard?</h3>
<br />
Three things: implementing hooks require Git-Fu, it's not easy to integrate functionality in a single hook, and it's not trivial to make them efficient.<br />
<br />
<h4>
Git-Fu</h4>
<br />
How many Git commands do you use? Ten? Twenty?<br />
<br />
Last time I counted there were 161 Git commands... Really! Run <span style="font-family: "Courier New",Courier,monospace;">git help -a</span> to see them all, and then some.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.petetheplumberny.com/plumber.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="200" src="http://www.petetheplumberny.com/plumber.jpg" width="191" /></a></div>
Most of these commands aren't needed for your daily workflow. The ones you use directly (add, commit, checkout, branch, fetch, push, etc.) are part of a class of commands called <a href="http://git-scm.com/book/ch9-1.html"><i>porcelain</i></a>, of which there are just a few. The majority of Git's commands belong to another class called <i>plumbing</i>. Those are the building blocks with which some porcelain commands are constructed, and they allow you to really get into Git's innards to investigate and poke around in the repository.<br />
<br />
You don't need to know about the plumbing while you're just using Git as a high level version control tool. But as soon as you start to write hooks you have to learn some of the esoteric and fascinating plumbing commands. That's what I call Git-Fu. You don't need to be a Git master, but you're gonna need a little Git-Fu to be a proficient hook developer.<br />
<br />
<h4>
Integration</h4>
<br />
There are 20 different hooks, but each repository has just one of each. Suppose you already have a cool pre-receive hook in your project's central repository to check against coding standards violations and you stumble upon an awesome hook at GitHub to check the formatting of commit log messages. You would like to use both to guarantee the high quality of your project's commits. However, you can't use them both "as is" because <a href="http://en.wikipedia.org/wiki/Highlander_(film)">there can be only one</a> pre-receive hook in the repository.<br />
<br />
One solution is to integrate the two hooks into a third one implementing both checks. This can be easy or hard, depending on the complexities of both hooks. Of course, if each one is written in a different programming language, the integration would be tantamount to re-implementing one into the other.<br />
<br />
A more general solution is to implement a "hook driver", i.e. a script which would invoke a set of other scripts in turn, passing to them the same parameters, checking their exit values, and exiting accordingly. The one thing that makes this solution non-trivial is the fact that some Git hooks (viz. pre-push, pre-receive, and post-rewrite) also get information from their standard input. So, the driver has to read all the input and then feed it to each one of the other scripts in turn.<br />
<br />
Anyway, standard Git doesn't have a ready solution for the need to invoke different programs in one hook.<br />
<br />
<h4>
Efficiency</h4>
<br />
Hooks invoked locally usually don't have to be particularly efficient. However, the hooks in your Git central server may be invoked much more frequently, even more so if your server serves many repositories for a large group of developers.<br />
<br />
Moreover, if your're using "hook drivers", each hook may be invoking many processes to perform its duties. Since most hooks are implemented as scripts, just the startup times of the interpreters can have a significant impact in the overall utilization of your server. (If you're interested in comparing programming languages startup times, I've <a href="http://blog.gnustavo.com/2013/07/programming-languages-startup-times.html">blogged about it</a> recently.)<br />
<br />
Yet another issue that may affect the efficiency of your hooks is that most of them have to invoke one or more of Git's plumbing commands to grok information about the repository and be able to process it and take action. If you have integrated many scripts behind a driver, most of them may be invoking the same Git command to grok the same information over and over again. Since they're in different processes and unaware of each other, they can't cache the information.<br />
<br />
<h3>
And the solution is...</h3>
<br />
Well, not "the", but "a" solution to alleviate the above-mentioned problems would be to come up with a framework for implementing Git hooks. Such a framework should provide an easier API to get the hook parameters and to invoke the plumbing. It also should implement the hook driver concept directly. And it should also allow for some kind of caching of information about the repository, minimizing the need to invoke Git commands redundantly.<br />
<br />
Guess what? There is at least one such framework. It's <a href="https://metacpan.org/module/Git::Hooks">Git::Hooks</a>. From yours truly.<br />
<br />
I should like to say a few things about it in the forthcoming posts... <br />
<br />Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com0tag:blogger.com,1999:blog-8641965788868337740.post-41763262193681554152013-07-20T22:30:00.000-03:002019-04-14T16:08:53.455-03:00Programming languages startup times - 2013 roundupI just revised <a href="http://blog.gnustavo.com/2012/06/programming-languages-start-up-times.html">the study I did a year ago about programming languages startup times</a>. It all started because I was writing some small script that would be frequently invoked and I wanted to know how did the startup times of Bash and Perl compare against each other. The results were not at all what I expected and I extended the investigation to other languages. The main conclusion for me was that Bash and Perl had very similar startup times, which let me stick with Perl, much to my delight.<br />
<br />
<a href="http://blog.gnustavo.com/2012/06/programming-languages-start-up-times.html">That post</a> received some attention this week due to my refering to it in <a href="http://szabgab.com/compiling-perl-python-and-ruby.html">another blog</a>, which made me want to repeat it to see if anything has changed in the meantime and to do it a little bit more properly. Also, I got some feedback and suggestions to extend it even further. So, in order to make it easier for me to repeat it and, perhaps, to incent people to replicate it in other platforms and with other languages, I've written a <a href="https://github.com/gnustavo/startup-times">simple script</a><a href="http://www.blogger.com/null"> called startup-times</a> to automate the benchmark process.<br />
<br />
The script is written in Perl (you guessed it!) and uses the <a href="https://metacpan.org/module/Benchmark">Benchmark</a> module to calculate the timings. This time I investigated 12 programming languages, two compiled (C and Java) and 10 interpreted. Running it on my laptop, which is still the same I used a year ago, a Dell Latitude E6410, now running Lubuntu 13.10, I got this:<br />
<br />
<blockquote class="tr_bq">
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">$ ./startup-times</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">Bash: GNU bash, versão 4.2.45(1)-release (x86_64-pc-linux-gnu)</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> timethis for 1: 10.1873 wallclock secs ( 0.08 usr + 0.92 sys = 1.00 CPU) @ 3840.00/s (n=3840)</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"></span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">C: gcc (Ubuntu/Linaro 4.7.3-1ubuntu1) 4.7.3</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> timethis for 1: 5.09504 wallclock secs ( 0.09 usr + 1.04 sys = 1.13 CPU) @ 3964.60/s (n=4480)</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"></span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">Java: javac 1.7.0_25</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> timethis for 1: 304.648 wallclock secs ( 0.14 usr 1.05 sys + 246.21 cusr 52.60 csys = 300.00 CPU) @ 12.80/s (n=3840)</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"></span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">JavaSun: javac 1.7.0_25</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> timethis for 1: 208.54 wallclock secs ( 0.11 usr 0.96 sys + 159.18 cusr 44.00 csys = 204.25 CPU) @ 17.55/s (n=3584)</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"></span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">Ksh: version sh (AT&T Research) 93u+ 2012-08-01</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> timethis for 1: 9.63142 wallclock secs ( 0.07 usr + 1.02 sys = 1.09 CPU) @ 3793.58/s (n=4135)</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"></span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">Lua: Lua 5.2</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> timethis for 1: 7.12142 wallclock secs ( 0.12 usr + 0.98 sys = 1.10 CPU) @ 3258.18/s (n=3584)</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"></span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">PHP: PHP 5.4.9-4ubuntu2.2 (cli) (built: Jul 15 2013 18:23:35) </span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> timethis for 1: 44.1422 wallclock secs ( 0.03 usr 1.07 sys + 23.97 cusr 13.64 csys = 38.71 CPU) @ 86.80/s (n=3360)</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"></span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">Perl: This is perl 5, version 14, subversion 2 (v5.14.2) built for x86_64-linux-gnu-thread-multi</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> timethis for 1: 11.7166 wallclock secs ( 0.09 usr + 1.05 sys = 1.14 CPU) @ 3627.19/s (n=4135)</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"></span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">Python: Python 2.7.4</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> timethis for 1: 55.0902 wallclock secs ( 0.12 usr 1.01 sys + 31.30 cusr 15.82 csys = 48.25 CPU) @ 69.64/s (n=3360)</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"></span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">Ruby: ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> timethis for 1: 68.0358 wallclock secs ( 0.02 usr 1.08 sys + 45.19 cusr 13.79 csys = 60.08 CPU) @ 63.91/s (n=3840)</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"></span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">TCL: TCL 8.5</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> timethis for 1: 18.4099 wallclock secs ( 0.17 usr 0.88 sys + 5.37 cusr 6.38 csys = 12.80 CPU) @ 233.28/s (n=2986)</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"></span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">Tcsh: tcsh 6.18.01 (Astron) 2012-02-14 (x86_64-unknown-linux) options wide,nls,dl,al,kan,rh,nd,color,filec</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> timethis for 1: 26.4094 wallclock secs ( 0.11 usr 0.91 sys + 6.70 cusr 6.76 csys = 14.48 CPU) @ 231.98/s (n=3359)</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"></span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">Zsh: zsh 5.0.0 (x86_64-unknown-linux-gnu)</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> timethis for 1: 15.1896 wallclock secs ( 0.11 usr 0.99 sys + 0.50 cusr 0.82 csys = 2.42 CPU) @ 1586.78/s (n=3840)</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"></span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"></span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">LANGUAGE CALLS/s NULL(ms) SCORE</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> C 879.286 1.137 1.000</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> Lua 503.271 1.987 1.747</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> Ksh 429.324 2.329 2.048</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> Bash 376.941 2.653 2.333</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> Perl 352.917 2.834 2.491</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> Zsh 252.805 3.956 3.478</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> TCL 162.196 6.165 5.421</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> Tcsh 127.190 7.862 6.913</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> PHP 76.118 13.138 11.552</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> Python 60.991 16.396 14.417</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> Ruby 56.441 17.718 15.579</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> JavaSun 17.186 58.186 51.163</span></span><br /><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> Java 12.605 79.335 69.759</span></span></blockquote>
I think a graph makes some things clearer.<br />
<br />
<img alt="" src="data:image/png;base64,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" /> <br />
<br />
There are a few things to notice. The first one is that <a href="http://www.lua.org/">Lua</a> beat all other interpreted languages. Rob Hoelz urged me to include it, already predicting this. I'm embarrassed to confess that I don't know much about Lua, even though it's a language with roots in Brazil.<br />
<br />
All shells (ksh, bash, zsh, and tcsh) have good and comparable startup times. Among the heavier scripting languages just Lua, Perl, and TCL are in the same ballpark. I've left Tcsh out of the green group because it's the slowest and <a href="http://www.faqs.org/faqs/unix-faq/shell/csh-whynot/">nobody should program in csh, anyway</a>.<br />
<br />
I've put PHP, Python, and Ruby in the yellow group. Their median startup time is six times higher than the green group median. So, for instance, in terms of performance alone for small and frequently used scripts this means that you can get six times more bang for buck with Perl than with Python or Ruby. :-)<br />
<br />
Java is another story. I even tried two different JDKs: the OpenJDK that comes with Ubuntu and the <strike>Sun</strike>Oracle JDK to see how much they differ. Not much. Both crawl in comparison with all other languages. <a href="https://www.google.com.br/search?q=java+startup+time">There seems to be a fair amount of discussion about this "problem"</a>. Even in <a href="http://www.mii.lt/olympiads_in_informatics/pdf/INFOL073.pdf">academia</a>. But I couldn't find a solution. It seems that Java simply isn't cut for this particular niche of programming.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com0tag:blogger.com,1999:blog-8641965788868337740.post-8851005202616672542013-02-09T12:40:00.000-02:002013-07-18T17:21:51.535-03:00Expressões regulares cruzadas<p>Ei, essa sim é uma brincadeira "nerd". :-)</p><p>O <a href="https://twitter.com/oreio/status/299879892781248512">Aurélio Jargas postou o desafio no twitter ontem</a> e ficou de publicar a resolução depois do Carnaval. Eu não consegui esperar:</p><p><a href="http://gnustavo.files.wordpress.com/2013/02/20130209_1414491.jpg"><img class=" wp-image" id="i-295" alt="Image" src="http://gnustavo.files.wordpress.com/2013/02/20130209_1414491.jpg?w=710" width="426" height="320" /></a></p><p> </p><p>Pra quem quiser resolver sozinho, <a href="http://www.coinheist.com/rubik/a_regular_crossword/grid.pdf">baixe o tabuleiro</a>.</p>Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com1tag:blogger.com,1999:blog-8641965788868337740.post-39790111286742347572012-12-21T12:14:00.000-02:002019-04-14T16:08:53.451-03:00"This is the end of the world"<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/LF5GlYlC0K4?feature=player_embedded' frameborder='0'></iframe></div>
<br />
<br />
If only I were a little more credulous, I'd think this had to be a sign of something.<br />
<br />
I'm a fan os Muse's early albums but I've never bought one. Until today, while I was looking for a present for my dad and I stumbled upon the Absolution CD and decided to buy it.<br />
<br />
When I put it on in the car, while driving home, it downed on me that those lyrics were so much apropriate for today. The day the world is supposed to end...<br />
<br />
"and our time is running out."Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com0tag:blogger.com,1999:blog-8641965788868337740.post-69669839079972445402012-09-26T12:00:00.000-03:002013-07-18T17:21:51.518-03:00Por que não dá pra baixar o "Pouer Point"?Vejam só a conversa que tive com minha filha via <em>chat</em>:<br/><blockquote><br/><div>14:44 Juliana: papi, eu entrei aqui pra estuda mais eu to no winddows e quero ir no pouer point, mas nao tem power point, como eu baixo?</div><br/><div>14:46 eu: oi fofs. Não dá pra baixar o PowerPoint porque é um programa proprietário. Ele é pago. A gente não tem PowerPoint em casa.</div><br/><div>14:47 Juliana: e pq n pode baixar?</div><br/><div>14:47 eu: porque é ilegal. Tem que pagar pra Microsoft pra poder instalar o PowerPoint. Alguns programas são proprietários e pagos. Outros, como os do Linux, são "livres" e de graça. Esses a gente pode baixar.</div><br/><div>14:48 Juliana: qual é aquele do google memo?</div><br/><div>14:48 eu: É o Google Docs.</div><br/><div>14:49 Clique em "Disco" aí em cima do Gmail. Ele roda no Chrome mesmo</div><br/><div>14:49 Juliana: ok</div><br/><div>14:49 eu: Depois a gente conversa e eu te explico esse negócio de software proprietário e software livre, tá?</div><br/><div>14:50 Juliana: nao prescisa</div><br/><div>14:50 eu: Mas eu quero! Deixa, vai!</div><br/><div>14:50 Juliana: rsrrsrzsrrsrsrs</div></blockquote><br/><div>Será que ela vai me deixar explicar? Depois eu conto. :-)</div>Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com0tag:blogger.com,1999:blog-8641965788868337740.post-9734076731239515132012-07-05T14:30:00.000-03:002019-04-14T16:08:53.485-03:00Why I don't send email receipts<p>I don't usually let my email reader send out <a href="http://en.wikipedia.org/wiki/Email_tracking">email receipts</a>. If you have sent an email to me expecting to get a read- or a return-receipt, please don't. I don't mean to be rude. I simply don't like them. They're broken in several ways: they <a href="http://blog.emailaddressmanager.com/outlook/email_read_receipts.html">aren't reliable</a> and they <a href="http://en.wikipedia.org/wiki/Email_tracking#Privacy_issues">can be misused</a>. So, why bother?</p><p>If you want to know if and when I read your email, please, say so. A simple "Please, acknowlege this." at the end of the message will trigger an instant reply. But please, don't make it a part of your signature. Not every message needs an acknowlegement and if you always require them I'll soon start to avoid replying. Few things annoy me more than a signature with a "I look forward to your reply" (or an equivalent "Aguardo retorno" in Portuguese) in it. I think that's rude.</p><p> </p>Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com0tag:blogger.com,1999:blog-8641965788868337740.post-19899295653753298902012-06-28T20:28:00.000-03:002019-04-14T16:08:53.438-03:00Programming languages start-up timesI don't remember the last time I wrote a substantial program in anything but Perl. It fills almost all of my needs as a sysadmin and <i>diletante</i> programmer. But there are situations in which I write bash scripts. Two, to be precise.<br />
<br />
One situation in which I feel more like using bash than Perl is when the script is small and function as a driver to invoke other programs. Bash (any shell, in fact) syntax to invoke other programs is more succint than Perl's. As you know: <a href="http://www.paulgraham.com/power.html">succinctness is power</a>.<br />
<br />
The other situation, is when the script in question is short and is going to be invoked lots of times. In this case, I worry about its start-up time, because it may very well dominate the overall system performance. I always assumed that Perl's start-up time was much larger than any shell's start-up time. However, as <a href="http://en.wikiquote.org/wiki/Donald_Knuth">Knuth</a> wisely said: Premature optimization is the root of all evil. You should always verify your assumptions with a profiler or, at least, a stopwatch before investing in any optimization work.<br />
<br />
These days I'm studying the implementation of <a href="http://git-scm.com/book/en/Customizing-Git-Git-Hooks">Git hooks</a> and I'm constantly struggling to decide if I should write them in Perl or bash, because they tend to be frequently invoked when you setup a Git server serving lots of developers.<br />
<br />
So, I decided to check exactly that. What's the real difference between the start-up time of Perl and bash. My testing platform is bash. I simply timed one thousand invokations of bash and Perl telling them to do nothing. This is what I got in my Dell Latitude E6410 laptop running Ubuntu 12.04:<br />
<br />
<pre><span style="font-family: Courier New, Courier, monospace; font-size: x-small;">$ (time for i in `seq 1 1000`; do bash -c :; done) 2>&1 | grep real
real 0m2.858s
$ (time for i in `seq 1 1000`; do perl -e 0; done) 2>&1 | grep real
real 0m3.326s</span></pre>
<br />
Not that different at all, is it? Perl takes just 16% more time to do nothing than bash. I sure was expecting Perl to take much more time than bash. Of course, a script doing nothing isn't that useful, although it can inspire a blog post. But while in order to perform useful work a bash script needs to invoke other programs, a Perl script can do many things in a single process just by useing (sic) <a href="http://www.cpan.org/">Perl modules</a>. So, I guess that after starting-up behind a bash script, an equivalent Perl script is going to catch up and finish the run first almost all times.<br />
<br />
I found this very interesting. So much so that I decided to extend my investigations to other scripting and compiled languages as well. Just out of curiosity. But the results were startling.<br />
<br />
The other three main scripting languages fared much worse than Perl. I wasn't expecting such a huge difference:<br />
<br />
<pre><span style="font-family: Courier New, Courier, monospace; font-size: x-small;">$ (time for i in `seq 1 1000`; do ruby -e 0; done) 2>&1 | grep real
real 0m5.628s
$ (time for i in `seq 1 1000`; do python -c 0; done) 2>&1 | grep real
real 0m27.373s
$ (time for i in `seq 1 1000`; do echo exit | tclsh; done) 2>&1 | grep real
real 0m10.991s</span></pre>
<br />
Ruby is 1.7 times slower than Perl, TCL is 3.3 times slower, and Python is 8.2 times slower!<br />
<br />
What about compiled languages? They should be faster, right? Of course they are. Let's C:<br />
<br />
<pre><span style="font-family: Courier New, Courier, monospace; font-size: x-small;">$ cat >null.c <<EOF
#include <stdlib.h>
int main()
{
exit(0);
}
EOF
$ gcc -O -o null null.c
$ (time for i in `seq 1 1000`; do ./null; done) 2>&1 | grep real
real 0m1.185s</span></pre>
<br />
This is interesting, because I rekon that this C program must have one of the shortest possible start-up times. So we can use it as a yardstick with which to compare every other language.<br />
<br />
What about Java? I don't speak Java, so I googled "java helloworld", found a good example and stripped it of every non-essential work:<br />
<br />
<pre><span style="font-family: Courier New, Courier, monospace; font-size: x-small;">$ cat >Null.java <<EOF
public class Null
{
public static void main(String args[])
{
}
}
EOF
$ javac Null.java
$ (time for i in `seq 1 1000`; do java Null; done) 2>&1 | grep real
real 0m58.231s</span></pre>
<br />
What?!? Almost one minute for doing nothing one thousand times? I did it again and again just to be sure. I realize Java isn't a vanila compiled language. At least, not like C. The Java compiler generates byte codes that are interpreted by the JVM. But since scripting languages in general have to perform the source to bytecode conversion just before the interpretation I thought that Java would be at least a little faster than most. So much for enterprise languages...<br />
<br />
So, to sum it all up, here is the final score of the game:<br />
<br />
<div style="text-align: center;">
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<img alt="Image" class="size-full wp-image aligncenter" height="160" src="http://gnustavo.files.wordpress.com/2012/06/captura-de-tela-de-2012-06-28-232622.png?w=362" width="362" /><br /><br />
<div style="text-align: left;">
<b>--- Update ---</b></div>
<div style="text-align: left;">
<b><br /></b></div>
<div style="text-align: left;">
<a href="http://blog.gnustavo.com/2013/07/programming-languages-startup-times.html">Read a revised and extended version of this study performed a year later.</a><b><br /></b></div>
</div>
Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com1tag:blogger.com,1999:blog-8641965788868337740.post-35942928984481083022012-05-01T19:26:00.000-03:002019-04-14T16:08:53.448-03:00On being sure and right<p>Being sure isn't the same as being right.</p><p>Being sure is a state of mind whereas being right is a state of the Universe.</p><p>These things don't correlate as well as one would hope, unfortunately. So, next time you feel so cock sure about something, remember this and be humble.</p>Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com0tag:blogger.com,1999:blog-8641965788868337740.post-82706709334914291772012-04-26T07:15:00.000-03:002019-04-14T16:08:53.481-03:00What is Pinto?<p>I can't resist the fun of this... I was searching for some module in <a href="http://search.cpan.org/">CPAN</a> and serendipitously found out <a href="http://search.cpan.org/dist/Pinto/">Pinto</a>.</p><p>I'm almost sure the author doesn't speak Portuguese and doesn't know that <a href="http://www.priberam.pt/DLPO/default.aspx?pal=pinto">"pinto" means "dick" here in Brazil</a>.</p><p>As soon as you realize that a whole other dimension of meaning springs from <a href="http://search.cpan.org/dist/Pinto/lib/Pinto.pm#DESCRIPTION">Pinto's documentation</a>. How can you not giggle when you read that:</p><ul><li><em>Pinto supports several usage patterns</em></li><li><em>Pinto does not understand author permissions</em></li><li><em>Pinto supports team development</em></li><li><em>Pinto can be extended</em></li><li><em>Pinto is not secure</em></li></ul><div>:-)</div>Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com0tag:blogger.com,1999:blog-8641965788868337740.post-51749025611397018882012-04-21T19:12:00.000-03:002019-04-14T16:08:53.459-03:00From Google Code to GitHubFollowing a <a href="http://wiki.dandascalescu.com/essays/pita-threshold">large</a> <a href="http://en.wikipedia.org/wiki/Comparison_of_open_source_software_hosting_facilities#Popularity">and</a> <a href="https://github.com/blog/620-committing-like-crazy">serious</a> <a href="http://www.google.com/trends/?q=google+code,+github">trend</a>, I just moved all of my <a href="http://code.google.com/u/gustavo@gnustavo.com/">Google Code</a> projects to <a href="https://github.com/gnustavo">GitHub</a>.<a href="http://gnustavo.files.wordpress.com/2012/04/github-profile-small.png"><img class="alignright size-full wp-image-179" title="GitHub" src="http://gnustavo.files.wordpress.com/2012/04/github-profile-small.png" alt="" width="80" height="120" /></a><br/><br/>Can't wait for the upcoming pull requests. :-)Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com0tag:blogger.com,1999:blog-8641965788868337740.post-30357356154250359422012-03-31T07:10:00.000-03:002013-07-18T17:21:51.465-03:00Aos 45 do primeiro tempo...Aconteceu semana passada à noite, depois de eu ter passado uma hora e meia no super-mercado.<br/><br/>O contexto é relevante, pois pode explicar minha aparência cansada.<br/><br/>Saindo do super-mercado eu fui à farmácia comprar umas coisinhas. Ao chegar ao caixa a atendente me olha e pergunta:<br/><br/>- O senhor tem o cartão da farmácia?<br/><br/>- Acho que sim... você pode verificar pelo CPF?<br/><br/>- Pois não, senhor. O seu cartão é o "privilege"?<br/><br/>- Como assim? Qual é a diferença?<br/><br/>- É de aposentado ou é normal?<br/><br/>...<br/><br/>Hesitei uns dois segundos e respondi desanimado:<br/><br/>- É normal...<br/><br/>A moça não percebeu, mas o meu desânimo deve ter ficado muito claro.<br/><br/>Por trás dela havia uma janela na qual eu podia ver meu reflexo. Analisei minha fisionomia e meus cabelos brancos já dominantes nas laterais e constatei que a dúvida dela não era absurda.<br/><br/>Um dia isso ia ter que acontecer. Aconteceu na primeira vez que me chamaram de "tio" e de "senhor". Mas já faz tempo e esses termos já não me afetam. Agora, "aposentado"... ainda tenho mais de uma década pela frente pra chegar lá... acho que quando chegar eu já vou estar acostumado.Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com0tag:blogger.com,1999:blog-8641965788868337740.post-3034967817162288972012-01-27T20:01:00.000-02:002013-07-18T17:21:51.446-03:00Estudar é divertido!Estou estudando sobre trânsito porque levei 20 pontos na CNH e vou fazer prova amanhã. Pois é... de novo.<br/><br/>Não acho que eu dirija mal e nem de forma perigosa. Minhas multas foram todas de excesso de velocidade. Mas todas foram de limite de 60 km/h e eu estava sempre um pouco acima dos 70 km/h. Mas a regra é clara e eu não posso reclamar se sou distraído.<br/><br/><em>Anyway</em>... ler sobre leis de trânsito, placas, gravidade das infrações... digamos que isso não foi o ponto alto das minhas noites recentes. Mas fazer os simulados da prova, ah, isso sim é que é diversão. A maioria das perguntas é bem fácil. Mas também, as opções erradas são geralmente óbvias e muitas vezes hilárias. Vejam essa:<br/><p style="text-align:center;"><a href="http://gnustavo.files.wordpress.com/2012/01/simulado.png"><img class="wp-image-142 aligncenter" title="simulado" src="http://gnustavo.files.wordpress.com/2012/01/simulado.png" alt="" width="427" height="188" /></a></p>Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com2tag:blogger.com,1999:blog-8641965788868337740.post-78015115410847143262012-01-26T15:18:00.000-02:002013-07-18T17:21:51.439-03:00Adeus delicious. Bem-vindo diigo!Se você é um dos três persistentes que ainda me segue no <a href="http://delicious.com/gnustavo">delicious</a> saiba que este é o meu último bookmark naquele serviço. Estou abandonando o deliciou e passando a manter meus bookmarks no <a href="http://www.diigo.com/user/gnustavo">diigo</a> apenas. Se quiser continuar a me seguir, o feed de meus bookmarks passa a ser <a href="GTI00141784">http://www.diigo.com/rss/user/gnustavo</a>.<br/><br/>Comecei a usar o delicious em dezembro de 2004 quando ele ainda se chamava del.icio.us. Nesses sete anos cadastrei quase 8.000 links lá e por muito tempo fui um usuário satisfeito. No ano passado ele passou por uma reformulação e, coincidentemente ou não, a extensão do Chrome que eu usava para postar os links estava ficando cada vez mais lenta. Acabei encontrando o <a href="diigo.com">Diigo</a> por acaso e resolvi experimentá-lo porque diziam que era mais rápido. E é, realmente. Apesar de que pra postar um link com tags você tem que dar dois cliques a mais, o que me irritou no início. Mas pelo menos eu não tinha que ficar esperando a janela do delicious abrir pra poder registrar as tags. O mais legal é que o Diigo permite que eu cadastre minha conta delicious nele de modo que a cada novo link que eu posto no Diigo ele o reposta (eta palavrinha feia!) no delicious automaticamente.<br/><br/>Fiquei nesse esquema por alguns meses e estava até pensando em voltar a experimentar a extensão do delicious pra ver se já tinham resolvido o problema de desempenho quando descobri que o Diigo oferece várias outras funções além do registro de tags e descrição dos links. A mais legal é a possibilidade de marcar (<em>to highlight</em>) trechos da página HTML que eu quero postar. As marcas aparecem com fundo amarelo pra mim e o conteúdo marcado aparece como comentários abaixo do link na página do Diigo. E eu posso até criar um <em>shortURL</em> específico pra um link e publicá-lo para que outras pessoas possam ver a página junto com minhas marcas.<br/><br/>Além das marcas eu também posso colocar comentários na página. Em pontos específicos. É muita funcionalidade por um precinho tão baratinho. (Dica: é de graça.)<br/><br/>É isso aí. Obrigado, delicious, por todos esses anos de bons serviços prestados. Mas é o que eu sempre <strong>diigo</strong>: não me responsabilizo caso você fuce no meu <strong>delicious</strong>. :-)Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com0tag:blogger.com,1999:blog-8641965788868337740.post-15010238498118932652011-09-22T18:25:00.000-03:002013-07-18T17:21:51.432-03:00Quer uma sugestão?<blockquote>Tenho medo de um escorpião<br/>Morder a minha mão.<br/><br/>Apesar de pequeno<br/>Ele tem muito veneno.<br/><br/>Sua picada é dolorosa<br/>E muito poderosa.<br/><br/>Quer uma sugestão?<br/>Fique longe do escorpião.<br/><br/>-- Juliana Loureiro Chaves</blockquote>Gustavo Chaveshttp://www.blogger.com/profile/01600149907557194828noreply@blogger.com0