måndag, mars 10, 2008

Humor

Detta är humor.














Sa jag att detta är humor?

lördag, mars 08, 2008

Programmerare utan gränser

På jobbet har en "studiecirkel" med det smålustiga namnet Programmerare utan gränser startats. Lite då och då får de som är med en uppgift att lösa; man får lösa den hur man vill (så länge det blir rätt :)) och i vilket språk som helst. Jag är självklart en av de gränslösa programmerarna!

Den första uppgiften var att översätta tal till ord, exempelvis "432" blir "fyrahundratrettiotvå". Programmet ska klara av alla tal mellan 0 och 10^12. Vilket programmeringspråk passar bäst för detta? Commodore 64 BASIC v2 så klart! 38911 basic bytes of freedom!

Min lösning är som följer:
  1. Lagra talet som ska "ordifieras" i NUMBER$
  2. Splitta NUMBER$ i grupper om tre, exempel "1" -> [1], "1234" -> [1, 234], "1000100" -> [1, 0, 100]
  3. Ordifiera varje sådan grupp.
  4. Konkatenera grupperna (med rätt suffix, exempelvis "miljoner", "tusen").
  5. Siste men inte minst hanteras jobbiga specialfall genom att byta ut en substräng mot en annan.
Punkt 5 ovan kan behöva lite mer förklaring. Algoritmen 1-4 ger strängar i stil med "etttusen", "ettmiljoner", etc, vilket självklart inte är rätt. Det är dock bara några få specialfall att hantera så det är lätt att helt enklet söka igenom strängen man fått från steg 4 och ersätta det som är fel med det som det borde vara. Exempel: "ettmijljoneretttusenett" blir "enmiljonettusenett".

För den som är väldigt nyfiken så följer källkoden här:

100 REM set test=0 if you don't want the test to be run (test=1 otherwise)
110 TEST=0
200 REM read data for 0-19 into t019$()
210 GOSUB 10000
220 REM read data for tens (0-90) into ttens$()
230 GOSUB 10100
240 REM read data for 1000, miljon, etc
250 GOSUB 10200
300 REM test method 'split'
310 IF TEST=1 THEN GOSUB 1100
320 REM test method 'wordify0to999'
330 IF TEST=1 THEN GOSUB 2200
340 REM test method 'wordifyall'
350 IF TEST=1 THEN GOSUB 3500
360 REM test method 'replace'
370 IF TEST=1 THEN GOSUB 6000
380 REM test method 'main'
390 IF TEST=1 THEN GOSUB 8000
900 REM the actual program starts here
910 TEST=0
920 PRINT "ange tal" : INPUT NUMBER$
930 GOSUB 7000
940 PRINT M$
999 END
1000 REM ------ method 'split' ------
1003 REM splits a string in groups of thee. example: "9876542" -> [9, 876, 542]
1005 REM in: number$, out: groups(), ngrp
1010 IF TEST=1 THEN PRINT " test: splitting <"+NUMBER$+">."
1020 DEF FN MOD3(X)=X-INT(X/3)*3
1030 IF FNMOD3(LEN(NUMBER$))=0 THEN 1050
1040 NUMBER$="0"+NUMBER$ : GOTO 1030
1050 FOR I = 1 TO LEN(NUMBER$) STEP 3
1060 : GROUPS(INT(I/3))=VAL(MID$(NUMBER$, I, 3))
1070 NEXT I
1080 NGRP=INT(I/3)-1
1090 RETURN
1100 REM - test case for method 'split'
1110 NUMBER$="2" : GOSUB 1000
1120 IF GROUPS(0) <> 2 GOTO 30000
1130 IF NGRP <> 0 GOTO 30000
1140 NUMBER$="19" : GOSUB 1000
1150 IF GROUPS(0) <> 19 GOTO 30000
1160 IF NGRP <> 0 GOTO 30000
1170 NUMBER$="123" : GOSUB 1000
1180 IF GROUP(0) <> 123 GOTO 30000
1190 IF NGRP <> 0 GOTO 30000
1200 NUMBER$="54321" : GOSUB 1000
1210 IF GROUP(0) <> 54 GOTO 30000
1220 IF GROUP(1) <> 321 GOTO 30000
1230 IF NGRP <> 1 GOTO 30000
1240 NUMBER$="1234" :GOSUB 1000
1250 IF GROUP(0) <> 1 GOTO 30000
1260 IF GROUP(1) <> 234 GOTO 30000
1270 IF NGRP <> 1 GOTO 30000
1280 NUMBER$="9876543210" :GOSUB 1000
1290 IF GROUP(0) <> 9 GOTO 3000000
1300 IF GROUP(1) <> 876 GOTO 30000
1310 IF GROUP(2) <> 543 GOTO 30000
1320 IF GROUP(3) <> 210 GOTO 30000
1330 IF NGRP <> 3 GOTO 30000
1340 RETURN
2000 REM --- method 'wordify0to999' ---
2010 REM in: wo, out: wrd$
2020 REM example: 99 -> nitionio
2030 IF TEST=1 THEN PRINT " test: wordifying <" + STR$(WO) + ">."
2040 DEF FN FRST(X)=X-INT(X/10)*10
2050 DEF FN SCND(Y)=INT(Y/10)-FNTHRD(Y)*10
2055 DEF FN THRD(Z)=INT(Z/100)
2060 DEF FN THRD(Z)=INT(Z/100)
2070 WRD$=""
2080 IF WO <= 19 THEN WRD$=T019$(WO):RETURN 2090 IF FNSCND(WO) > 0 THEN WRD$=TTENS$(FNSCND(WO))
2100 IF FNFRST(WO) > 0 THEN WRD$=WRD$+T019$(FNFRST(WO))
2110 IF FNTHRD(WO) > 0 THEN WRD$=T019$(FNTHRD(WO))+"hundra"+WRD$
2190 RETURN
2200 REM - test method 'wordify0to99' -
2210 WO=9 : GOSUB 2000
2220 IF WRD$ <> "nio" GOTO 30000
2230 WO=19 : GOSUB 2000
2240 IF WRD$ <> "nitton" GOTO 30000
2250 WO=20 : GOSUB 2000
2260 IF WRD$ <> "tjugo" GOTO 30000
2270 WO=90 : GOSUB 2000
2280 IF WRD$ <> "nittio" GOTO 30000
2290 WO=21 : GOSUB 2000
2300 IF WRD$ <> "tjugoett" GOTO 30000
2310 WO=0 : GOSUB 2000
2320 IF WRD$ <> "noll" GOTO 30000
2330 WO=55 : GOSUB 2000
2340 IF WRD$ <> "femtiofem" GOTO 3000
2350 WO=999 : GOSUB 2000
2360 IF WRD$ <> "niohundranittionio" GOTO 30000
2370 WO=101 : GOSUB 2000
2380 IF WRD$<> "etthundraett" GOTO 30000
2390 WO=500 : GOSUB 2000
2400 IF WRD$ <> "femhundra" GOTO 30000
2500 RETURN
3000 REM -- method 'wordifyall'
3010 REM in: groups(), ngrp, out: all$
3015 REM ngrp: max index of group()
3020 REM example: [9, 3] => "niotusentre"
3025 REM note: does not handle special cases, e.g., "ettusen" and "enmiljon"
3027 REM correctly. this is done later by a special "correction" function.
3030 IF TEST=1 THEN PRINT " test: wordifying all <" + STR$(GROUPS(0)) + "...>"
3050 ALL$=""
3080 FOR IDX=0 TO NGRP
3090 : GOSUB 4000
3200 : ALL$=ALL$+WRD$
3210 : IF WRD$ <>"" THEN ALL$=ALL$+TPOT$(NGRP-IDX)
3300 NEXT IDX
3400 RETURN
3500 REM test of method 'wordifyall'
3510 GROUPS(0)=90 : NGRP=0 : GOSUB 3000
3520 IF ALL$ <> "nittio" GOTO 30000
3530 GROUPS(0)=2 : GROUPS(1)=0 : NGRP=1 : GOSUB 3000
3540 IF ALL$ <> "tvatusen" GOTO 30000
3550 GROUPS(0)=9 : GROUPS(1)=51 : NGRP=1 : GOSUB 3000
3560 IF ALL$ <> "niotusenfemtioett" GOTO 30000
3570 GROUPS(0)=1 : GROUPS(1)=11 : GROUPS(2)=900 : NGRP=2 : GOSUB 3000
3580 IF ALL$ <> "ettmiljonerelvatusenniohundra" GOTO 3000
3590 GROUPS(0)=9 : GROUPS(1)=0 : GROUPS(2)=0 : NGRP=2 : GOSUB 3000
3600 IF ALL$ <> "niomiljoner" GOTO 30000
3610 GROUPS(0)=1 : GROUPS(1)=0 : NGRP=1 : GOSUB 3000
3620 IF ALL$ <> "etttusen" GOTO 30000
3630 GROUPS(0)=6 : GROUPS(1)=5 : GROUPS(2)=4 : GROUPS(3)=3 : GROUPS(4)=2: NGRP=4
3640 GOSUB 3000
3650 IF ALL$ <> "sexbiljonerfemmiljarderfyramiljonertretusentva" GOTO 30000
3700 RETURN
4000 REM method that works like 'wordify0to999' but returns "" instead of "noll"
4010 REM if it is a three-group > 1000.
4020 REM in: groups(), idx
4025 REM out: wrd$
4030 REM idx = index of group
4040 WO=GROUPS(IDX) : WRD$=""
4050 IF IDX=0 THEN GOSUB 2000 : RETURN
4060 IF WO > 0 THEN GOSUB 2000 : RETURN
4070 RETURN
5000 REM -- method 'replace'
5005 REM -- function that replaces a substing (s$) of another string (m$)
5010 REM -- with a replacement string (r$)
5020 REM -- returns: m$ - the modified string
5030 IF TEST=1 THEN PRINT " test: replace <"+S$+","+M$+","+R$+">"
5100 FOR SI=1 TO LEN(M$)-LEN(S$)+1
5120 : IF MID$(M$, SI, LEN(S$))=S$ THEN GOTO 5500
5130 NEXT SI
5140 RETURN
5500 REM replace mid$(m$, si, len(s$)) with r$
5510 M$=LEFT$(M$, SI-1) + R$ + MID$(M$, SI+LEN(S$), LEN(M$)-SI-LEN(S$)+1)
5520 RETURN
6000 REM test-case for method 'replace'
6010 M$="etttusen" : S$="ttt" : R$="tt" : GOSUB 5000
6020 IF M$ <> "ettusen" GOTO 30000
6030 M$="ettmiljonertio" : S$="ettmiljoner" : R$="enmiljon" : GOSUB 5000
6040 IF M$ <> "enmiljontio" GOTO 30000
6050 M$="elva" : S$="etttusen" : R$="ettusen" : GOSUB 5000
6060 IF M$ <> "elva" GOTO 30000
6070 M$="enmiljonetttusen" : S$="etttusen" : R$="ettusen" : GOSUB 5000
6080 IF M$ <> "enmiljonettusen" GOTO 30000
6500 RETURN
7000 REM -- the main function. argument number$ is wordified (returned in m$)
7010 IF TEST=1 THEN PRINT " test: main <"+NUMBER$+">"
7020 REM call 'split'. returns groups(), ngrp
7030 GOSUB 1000
7040 REM call 'wordify0tall". returns all$. arguments: groups(), ngrp
7050 GOSUB 3000
7060 REM handle special cases, e.g., "etttusen" should be "ettusen".
7070 REM this is done using the 'replace' function.
7080 REM arguments: m$, s$, r$. returns m$
7090 M$=ALL$ : S$="etttusen" : R$="ettusen" : GOSUB 5000
7100 S$="ettmiljoner" : R$="enmiljon" : GOSUB 5000
7110 S$="ettmiljarder" : R$="enmiljard" : GOSUB 5000
7120 S$="ettbiljoner" : R$="enbiljon" : GOSUB 5000
7500 RETURN
8000 REM test for the main function.
8010 NUMBER$="101" : GOSUB 7000
8020 IF M$ <> "etthundraett" GOTO 30000
8030 NUMBER$="1001001001000" : GOSUB 7000
8040 IF M$ <> "enbiljonenmiljardenmiljonettusen" GOTO 30000
8050 NUMBER$="2002002002000" : GOSUB 7000
8060 IF M$ <> "tvabiljonertvamiljardertvamiljonertvatusen" GOTO 30000
8999 RETURN
10000 REM read data for 0-19
10010 DIM T019$(19)
10020 FOR I=0 TO 19 : READ T019$(I)
10030 NEXT I
10040 RETURN
10100 REM read data fot 0, 10, 20, etc
10110 DIM TTENS$(9)
10120 FOR I=0 TO 9 : READ TTENS$(I)
10130 NEXT I
10140 RETURN
10200 REM read data for 1000, miljon...
10210 DIM TPOT$(4)
10220 FOR I=0 TO 4 : READ TPOT$(I)
10230 NEXT I
10240 RETURN
20000 REM -- data for t019$()
20010 DATA "noll", "ett", "tva", "tre", "fyra", "fem", "sex", "sju", "atta"
20020 DATA "nio", "tio", "elva", "tolv", "tretton", "fjorton", "femton"
20030 DATA "sexton", "sjutton", "arton", "nitton"
20100 REM -- data for ttens$()
20110 DATA "noll", "tio", "tjugo", "tretto", "fyrtio", "femtio", "sextion"
20120 DATA "sjutio", "attio", "nittio"
20200 REM -- data for 1000, miljon ...
20210 DATA "", "tusen", "miljoner", "miljarder", "biljoner"
29999 END
30000 PRINT "assertion error!"

Etiketter:

måndag, mars 03, 2008

Ett alldeles rent trasigt tangentbord

I lördags tvättade jag mitt tangentbord (inte detta, men ett annat jag köpte för ungefär ett år sedan). Tyvärr tvättade jag det aningen för noggrant, vilket ledde till att tangentbordet numera fungar som dörrstopp istället... Inte för att det var något speciellt bra tangentbord.

Hursomhelst, utan tangentbord kan man ju inte vara någon längre tid eftersom datorn inte funkar så bra utan det. Därför åkte jag i söndags ut till Tornby och köpte ett nytt. Tyvärr funkade det nya inte så speciellt bra eftersom min BIOS bara gillar PS/2-tangentbord (det nya var så klart ett USB-tangentbord). Detta gjorde att jag inte kunde välja operativsystem i GRUB när datorn startar, vilket gör att default-valet körs vilket är Linux. När väl Linux startat funkar dock tangentbordet perfekt.

Tyvärr är jag inte av-windowsfierad ännu och kör Windows mest hela tiden. Alla program jag kör är windowsprogram, med andra ord. Jag trivs därför inte så bra utan mina windowsprogram i Linux. Dessutom kan jag inte komma åt alla mina filer på Windows-partitionen av någon anledning. Konstigt, men orka lägga tid... icke. Jag tänker att när jag ändå är i Linux så kan jag passa på att uppdatera det och leka lite med det, för det ju lite småkul iaf.

Jag surfar fram att jag ska ställa in "Support for legacy USB devices" i BIOS för att få USB-tangentbordet att funka. Men hur gör jag det utan ett PS/2-tangenbord? Jaja, jag får väl låna ett tangentbord.

När jag med mitt lånade PS/2-tangentbord (tack Martin) fixat så att BIOS har stöd för gamla USB-mojänger så får en förfärlig upptäckt. Då jag uppdaterade Linux så gjordes tydligen en ändring i GRUB som gör att Windows inte kan startas från GRUBs meny. Detta problem har jag inte löst ännu, men jag misstänker att det inte är några större svårigheter.

Och allt detta för att jag ville få ett aningen renare tangentbord...