2015-09-03

Hello, world!

The classic first demonstration program.

pack [button .b -text "Hello, world!" -command exit]

In a Tcl/Tk shell such as wish, this program creates a window and places a plain, unstyled button on it. The text on the button is "Hello, world!" and when you click the button, the program exits.

If you just want to print the text inside the shell, try

puts "Hello, world!"

2015-09-01

Writing sequentially

The challenge is as follows.

1) Pick out code5, the second part of the last word on the last line of this data file:

line1 1 2 3 4 5 end-code1-line
line2 2 2 3 4 5 end-code2-line
line3 3 2 3 4 5 end-code3-line
line4 4 2 3 4 5 end-code4-line
line5 5 2 3 4 5 end-code5-line

2) Do so by executing commands written sequentially ("pipelining" the data from one command to the next).

What we need to do is to read the data from the file, remove ("trim") whitespace from the beginning and end of it, split it into lines, pick the last line, split it into words, pick the last word, split it at the dashes, choose the second element of it.

The most natural way to solve this in Tcl is something like

package require fileutil

set data [string trim [::fileutil::cat lines.txt]]
set line [lindex [split $data \n] end]
set word [lindex [split $line] end]
lindex [split $word -] 1

This is not written in sequential order. The first command to be executed on each line is the one in the innermost brackets, then the command in the enclosing set of brackets, and so on.

The same code in sequential order looks like this:

package require fileutil

set v lines.txt
set v [::fileutil::cat $v]
set v [string trim $v]
set v [split $v \n]
set v [lindex $v end]
set v [split $v]
set v [lindex $v end]
set v [split $v -]
lindex $v 1

This works, but this is Tcl. We can abstract away a lot of detail here.

One idea is to enumerate the commands to be applied to the data and execute them in a loop:

proc seq {v cmds} {
    foreach cmd $cmds {
        set v [{*}$cmd $v]
    }
    set v
}

seq lines.txt {::fileutil::cat {string trim}}

But that only works as far as the string trim. The next command, to split the lines, requires a second argument.

We can solve that by creating command aliases:

interp alias {} lines {} apply {v {split $v \n}}
interp alias {} words {} split
interp alias {} last {} apply {v {lindex $v end}}

One of those, words, is unnecessary since that invocation doesn't need any further argument. It makes the code a bit clearer, though.

The other aliases work by rewriting invocations: the invocation last {a b c} is rewritten as lindex {a b c} end and so on.

Now we can perform the processing as far as seq lines.txt {::fileutil::cat {string trim} lines last words last}. The last two commands could have been aliased the same way, but let's be a bit more creative. If we define the convention that a command that contains the character / has an attached argument, we can let our code do the same rewriting as the aliases do.

proc seq {v cmds} {
    foreach cmd $cmds {
        if {[string match */* $cmd]} {
            set cmd [list apply [list v [regsub / $cmd { $v }]]]
        }
        set v [{*}$cmd $v]
    }
    set v
}

The if looks a little busy, but it's actually quite straightforward. The condition is that if $cmd contains a slash character somewhere (the asterisks state that there may be, but doesn't have to be, characters before and after the slash). Then a regsub command replaces that slash with the text { $v }, i.e. space, dollar, v, space. The resulting string is wrapped in a list with the text v as first element, and then that is wrapped in a list with apply as first element. The result becomes the rewritten command: if we had lindex/end, we now have {apply {v {lindex $v end}}}, which can be executed by set v [{*}$cmd $v].

And we can finally write the procedure sequentially. Compare with the second program above.

seq lines.txt {
    ::fileutil::cat
    {string trim}
    lines
    last
    words
    last
    split/-
    lindex/1
}

2015-08-18

Han säger NEJ till Pride

This post is about Swedish theo-politics and therefore in Swedish.

Jag skrev en kommentar till den här bloggposten, men den fastnade, tydligen för evigt, i granskningen. Uppenbarligen tyckte Skredsvik att det var bekvämare att få beröm av likasinnade än att hantera kritik. Så jag postar min kommentar här i stället:

Ja, Pride handlar i grund och botten om sexualitet. Sexualitet är något som är viktigt för alla, men de människor vars sexualitet är i linje med den gällande sexualmoralen tror att de inte är lika "sexfixerade" som de som står utanför den, därför att de aldrig behövt kämpa med att acceptera sig själva och kämpa mot samhällets fördomar för att få uttrycka och leva ut den sexualitet de har.

Pride finns till för att de människor som inte är heterosexuellt monogama ska få uppleva samma glädje och stolthet över att de är som de är som heterosexuellt monogama ständigt får. Pride vill bekräfta dessa människor på motsvarande sätt som vår kultur hela tiden bekräftar kärleken mellan man och kvinna.

Pride är inte till för att värna om och lyfta upp kärleken till Jesus, som du efterlyser, för den kärleken handlar inte om sexualitet utan om en självvald fixering. Pride behöver inte ens värna om eller lyfta fram kärleken mellan man och kvinna, för den kärleken har sin Pride-parad varenda dag på året.

Du får gärna för mig säga nej till Pride, och om du kommer med vettig kritik mot Pride så är det välkommet: konstruktiv kritik är alltid bra. Men den här bloggposten går längre än så. Du är extremt noggrann i dina formuleringar för att inte framstå som fördomsfull och oärlig, men lyckas inte helt.

Du skriver att stödet från "politiker, tidningar, kyrkoledare, tv-bolag osv" handlar om att "följ[a] efter i strömmen och [...] vänd[a] kappan efter vinden". Du försöker alltså få det att framstå som om alla dessa saknar både rationella skäl till att stödja Pride och den moraliska hållning som annars skulle få dem att ta avstånd från Pride. Det är ganska magstarkt att framhäva dig själv som klokare och mer moraliskt högtstående än samtliga dessa.

Du skriver att de som "har en annan syn på sexualitet än majoriteten blir ofta anklagade för att vara bakåtsträvande, hatiska, homofober, och allt möjligt" och framställer dig alltså som en förtryckt sexuell minoritet. Jag gissar att du är vad som idag kallas "cis-het-mono", alltså att du lever i samma kön som du är född i och praktiserar heterosexuell, monogamisk sexualitet. Detta innebär att du ingår i en majoritet, inte en minoritet. Att en stor del av denna majoritet idag har insett att det är fel att förtrycka HBTQ+-minoriteten innebär fortfarande inte att du plötsligt hamnat i en sexuell minoritet.

Du skriver att många menar att det "inte går att vara kritisk mot Pride och samtidigt värna om alla människors lika värde". OK, det är en åsikt som man kan ha, men den bygger inte på fakta: självklart kan man vara kritisk mot Pride och ändå vara engagerad i människors lika värde. "Kan det bli mer fel?" frågar du retoriskt. Ja, det kan bli mycket mer fel. T ex kan det bli fel på det sättet att du försöker göra Pride-rörelsen ansvarig för vad "många" säger om dem.

Du skriver att pga man har äktenskapssynen att "äktenskapet är för en man och en kvinna" så kan man i Sverige "få stå ut med hån, förtal, lögner och alla möjliga ogrundade anklagelser". Detta är djupt oärligt. Ingen överhuvudtaget förhindrar dig att ha en sådan äktenskapssyn, och ingenting drabbar dig på grund av att du har den eller ens pläderar för den. Det är när du försöker förhindra andra att frångå den äktenskapssynen som du riskerar att drabbas av, enligt min mening, ganska rimlig kritik.

Jag orkar inte bemöta allt ditt hat, jag väljer ut ett ord att påpeka: "översexualiserad". Ett klassiskt hatord som använts i många sammanhang, bl a mot svarta människor längre tillbaka i tiden. När det används mot HBTQ+-människor är det för att underkänna deras sexualitet, att säga att Görans kärlek till Lasse inte är en verklig, sund kärlek, utan att det handlar om att de är "översexualiserade" och på det viset börjat älska någon de inte borde älska.

När du skriver att "[a]ll kärlek är tydligen inte godkänd av Pride. Vissa människors kärlek är tydligen inte lika mycket värd som andras kärlek" är det ganska uppenbart att du applicerar dina egna värderingar på dem. Pride varken godkänner eller underkänner kärlek, och Pride värderar inte olika sorters kärlek mot varandra. Däremot läser man mellan raderna att enbart en sorts kärlek är godkänd i dina ögon, och att andra sorters kärlek är mindre värd. Är du beredd att bevisa att jag har fel, och skriva en bloggpost där du visar att du står för att alla former av kärlek är "godkända" och lika mycket värda?

2015-08-17

Ordered numeric sequences

I wrote an answer on Stackoverflow, but I think the question is going to go away soon, so I'll post an edited version of my answer here.

The question was about how to find out whether digits in a sequence were in ascending (increasing) order or in descending (decreasing order). The sequence 1 2 3 is in increasing order, and so is 11 5, but this order is non-strict, i.e. the same digit may appear more than once if there are no other digits in between.

One can use mathematical comparison operators to check whether sequences of digits are in order:

foreach n {12345 54321 35214} {
    set ns [split $n {}]
    if {[::tcl::mathop::<= {*}$ns]} {
        puts "$n is in ascending order"
    } elseif {[::tcl::mathop::>= {*}$ns]} {
        puts "$n is in descending order"
    } else {
        puts "$n is neither in ascending or descending order"
    }
}

The command ::tcl::mathop::<= (which also implements the <= operator in expr) can take an arbitrary number of values, preferably numeric, and returns 1 if those values are in non-strict ascending order. It can be called directly on some values:

::tcl::mathop::<= 1 2 3 4 5
# -> 1

If the values are in a list it needs to be expanded first:

set a {1 2 3 4 5}
# -> 1 2 3 4 5
::tcl::mathop::<= {*}$a
# -> 1

If the values are packed in a string it needs to be split into a list and then expanded:

set a 12345
# -> 12345
::tcl::mathop::<= {*}[split $a {}]
# -> 1

The call to <= can be made a little less cumbersome by adding its namespace to the places where the interpreter looks for commands:

namespace path ::tcl::mathop
<= 1 2 3 4 5

Documentation: foreachiflistmathopnamespaceputssetsplit{*}

2015-01-21

Verbosity

I read a blog post today, A Short Story About Verbosity, about writing a tech book. The publisher had asked the writer to go for at least 600 pages. I was reminded of a joke I used to make back when I was teaching C and C++. I would plop down the 272-page The C Programming Language on the desk and say "This is an exhaustive description of C". Then I'd let the ~980-page "The C++ Programming Language" fall with a sickening thud and say "This is the bare minimum you need to understand C++". (Don't worry, I didn't leave them with that: there are shortcuts into C++, and I did my best to point them out.)

Many times you can't really avoid verbosity in a tech book. In the best case, this is because the subject is extensive. In the worst case, it's because the subject is humongous.

I still hope to be able to take my programming book from plans to reality one day. Whatever it becomes, it won't be thick. I'd be surprised if it's more than 300 pages. If I can't say what I want to say in less pages than that, maybe I shouldn't bother. That might mean that no publisher will be interested, but there are other ways.