Most efficient way to convert RGB to int? - colors

I've encountered the following RGB-to-int method in various places:
//r,g, and b are bytes
int c = r;
c = (C << 8) | g;
c = (c << 8) | b;
return c;
But isn't it more efficient to say:
return (r << 16) | (g << 8) | r;
It seems like the first way results in a lot of unnecessary store/load instructions. Is there a reason everyone seems to prefer the first way over the second?

In the 2nd expression, you make small typo: twice used "r", and never "b". I assume, you meaning:
return (r << 16) | (g << 8) | b;
Regarding your question: result is very dependent on your computer architecture and compiler version. I build both versions with clang CC, FreeBSD OS, -O3.
Source code:
int rgb1(int r, int g, int b) {
int c = r;
c = (c << 8) | g;
c = (c << 8) | b;
return c;
}
int rgb2(int r, int g, int b) {
return (r << 16) | (g << 8) | b;
}
unsigned int rgb3(unsigned int r, unsigned int g, unsigned int b) {
return (r * 256*256) + (g * 256) + b;
}
Assembly code following:
rgb1:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
sall $8, %eax
orl 12(%ebp), %eax
sall $8, %eax
orl 16(%ebp), %eax
popl %ebp
ret
.size rgb1, .-rgb1
.p2align 4,,15
.globl rgb2
.type rgb2, #function
rgb2:
pushl %ebp
movl %esp, %ebp
movl 12(%ebp), %eax
movl 8(%ebp), %edx
sall $8, %eax
orl 16(%ebp), %eax
popl %ebp
sall $16, %edx
orl %edx, %eax
ret
.size rgb2, .-rgb2
.p2align 4,,15
.globl rgb3
.type rgb3, #function
rgb3:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
sall $8, %eax
addl 12(%ebp), %eax
sall $8, %eax
addl 16(%ebp), %eax
popl %ebp
ret
As you see, 1st function is 9 instructions, 2nd function is 10. Therefore, 1st seems little more efficient. But, in real life, it very depends on your compiler behavior.
For example, rgb3() uses many math operations, but compiler optimized it to something, very close to rgb1().

Related

How to use "repne scasb" in GAS assembly?

So I have written a program based on NASM that receives user input (two numbers to be exact), and then it realizes the addition and difference between the two numbers, and then it prints it back. The program works fine in NASM but I am having trouble with GAS. The subroutine that counts the length of a string using the repne scasb instruction is giving me a headache because of a segmentation fault.
I have checked the code for segmentation faults, and I have located the fault in the repne scasb line.
What I am basically doing is translating a NASM code I made into its respective GAS code. However, as I said before, it's giving me a segmentation fault. After I get the first number from the user, to be more specific.
.section .data
msg: .ascii "Insert a number: "
msgLen = .-msg
msg2: .ascii "Insert another number: "
msg2Len = .-msg2
errorMsg: .ascii "Error: invalid character!\n"
errorMsgLen = .-errorMsg
displaySuma: .ascii "The result of the addition is: "
displaySumaLen = .-displaySuma
displayResta: .ascii "The result of the difference is: "
displayRestaLen = .-displayResta
enterChar: .ascii "\n"
terminator: .byte 0
.section .bss
.lcomm num1, 8
.lcomm num2, 8
.lcomm buffer, 10
.lcomm buffer2, 10
.section .text
.global _start
_start:
call _clear #clear registers. Probably an useless routine
call _msg1 #Display msg1
call _num1 #Read num1
movl num1, %edi
call _lenString #ECX now has num1 length
lea (num1), %esi
call _stringToInt #EAX now has num1 in integer
movl %eax, %r15d #R15D now has the integer
call _msg2 #Display msg2
call _num2 #Read num2
xor %edi, %edi #Clear EDI
movl num2, %edi #Moving num2 to EDI register to call _lenstring
xor %ecx, %ecx #Clear ECX
call _lenString #ECX has num2 length
xor %esi, %esi #clear ESI
lea (num2), %esi
call _stringToInt #EAX now has integer value of num2
mov %eax,%r14d ###R14D has num2 now
#Addition
#r8d = num1 + num2
mov %r15d, %r8d
add %r14d, %r8d #R8D has num1 + num2
#Difference
#If num1 > num2 =======> r9d = num1 - num2
#If num1 < num2 =======> r9d = num2 - num1
cmp %r14d, %r15d
jg .greater
mov %r14d, %r9d
sub %r15d, %r9d #R9D has num1 - num2
jmp .next
.greater:
mov %r15d, %r9d
sub %r14d, %r9d #R9D has num2 - num1
jmp .next
.next:
mov %r8d, %eax #Sum is now at EAX to convert it to ascii characters
lea (buffer), %esi
call _intToString
#EAX ascii of the sum
mov %eax, %r10d #Using R10D to store the new string
mov %r9d, %eax #Difference result is now at EAX
lea (buffer2), %esi
call _intToString
#EAX has the pointer to the difference result.
mov %eax, %r11d #Storing string in R10D
xor %edi, %edi
xor %r15d, %r15d
xor %r14d, %r14d
mov %r10d, %edi
call _lenString #ECX length of sum string
mov %ecx, %r15d #R15D now has that value
call _suma #This prints the sum result
xor %edi, %edi #Clear EDI
mov %r11d, %edi
call _lenString #ECX has length of dif. string
mov %ecx, %r14d #R14D has that value
call _resta #Print dif. result
movl $1, %eax #End of the program
movl $0, %ebx
int $0x80
_stringToInt:
xor %ebx, %ebx
.next_digit:
movzxb (%esi), %eax
cmp $0x30, %eax #These 4 lines check for invalid characters
jb _errorMsg
cmp $0x39, %eax
ja _errorMsg
inc %esi
sub $0x30, %eax ###Sub 48 (converts to integer)
imul $10, %ebx
add %eax, %ebx #ebx = ebx*10 + eax
loop .next_digit #loop [ECX] times
mov %ebx, %eax
ret
_intToString:
add $10, %esi
mov (terminator),%esi
mov $10, %ebx
.next_digit1:
xor %edx, %edx
div %ebx
add $0x30, %edx ##
dec %esi
mov %dl, (%esi)
test %eax, %eax
jnz .next_digit1
mov %esi, %eax
ret
#######################################################################################################
_msg1:
movl $4, %eax #msg1 routine
movl $1, %ebx
movl $msg, %ecx
movl $msgLen, %edx
int $0x80
ret
_num1:
movl $3, %eax #Reads first number
movl $0, %ebx
movl $num1, %ecx
movl $8, %edx
int $0x80
ret
_msg2:
movl $4, %eax #msg2 display
movl $1, %ebx
movl $msg2, %ecx
movl $msg2Len, %edx
int $0x80
ret
_num2:
movl $3, %eax #Reads the next number
movl $0, %ebx
movl $num2, %ecx
movl $8, %edx
int $0x80
ret
_salir:
movl $1, %eax #Exit
movl $0, %ebx
int $0x80
_errorMsg:
movl $4, %eax #Error msg
movl $1, %ebx
movl $errorMsg, %ecx
movl $errorMsgLen, %edx
int $0x80
jmp _salir
_lenString:
xor %ecx, %ecx
not %ecx
xor %al, %al
mov $0xA, %al
cld
repne scasb #Segmentation fault is caused by this line
not %ecx
dec %ecx
ret
_suma:
movl $4, %eax
movl $1, %ebx
movl $displaySuma, %ecx
movl $displayRestaLen, %edx
int $0x80
movl $4, %eax
movl $1, %ebx
mov %r10d, %ecx
mov %r15d, %edx
int $0x80
movl $4, %eax
movl $1, %ebx
movl $enterChar, %ecx
movl $1, %edx
int $0x80
ret
_resta:
movl $4, %eax
movl $1, %ebx
movl $displayResta, %ecx
movl $displayRestaLen, %edx
int $0x80
movl $4, %eax
movl $1, %ebx
mov %r11d, %ecx
mov %r14d, %edx
int $0x80
movl $4, %eax
movl $1, %ebx
movl $enterChar, %ecx
movl $1, %edx
int $0x80
ret
_clear:
xor %eax, %eax
xor %ebx, %ebx
xor %ecx, %ecx
xor %edx, %edx
xor %esi, %esi
xor %edi, %edi
xor %r8d, %r8d
xor %r9d, %r9d
xor %r10d, %r10d
xor %r11d, %r11d
xor %r14d, %r14d
xor %r15d, %r15d
ret
I am using this makefile to create the .o and .exe files (Given by my professor):
#*************************************************
# Executable name : hola
# Version : 2.0
# Created date : February 12, 2019
# Authors :
# Eduardo A. Canessa M
# Description : simple makefile for GAS
# Important Notice: To be used for GAS only
#*************************************************
#change the name "ejemplo" for the name of your source file
name=addSubInteger
#program to use as the assembler (you could use NASM or YASM for this makefile)
ASM=as
#flags for the assember
#ASM_F= #*** place here flags if needed ***
#program to use as linker
LINKER=ld
#link executable
$(name): $(name).o
$(LINKER) -o $(name) $(name).o
#assemble source code
$(name).o: $(name).s
$(ASM) $(ASM_F) -o $(name).o $(name).s
There is a segmentation fault error after the program reads the first user input.
This is the NASM code of my program (Hope you don't mind the spanish comments in it, but it's essentially the same program as the one written on GAS).
I know I have made some next level spaghetti code, but this is the solution I came to.
movl num1, %edi
call _lenString #ECX now has num1 length
lea (num1), %esi
call _stringToInt #EAX now has num1 in integer
The first instruction does not load the address in %edi. You can use lea like you did for the call to _stringToInt that follows next. Or if you care about a shorter encoding then write mov $num1, %edi.
lea (num1), %edi
call _lenString #ECX now has num1 length
The same problem exists for the second number:
movl num2, %edi SAME PROBLEM
xor %ecx, %ecx
call _lenString
The _intToString subroutine has 2 problems!
You destroy the address in %esi by writing a random value in it.
You (try to) write in memory beyond the buffer that was reserved via .lcomm buffer, 10. This will destroy the first byte in buffer2.
Since converting a 32-bit integer can produce (at most) 10 characters, you will need to enlarge your buffer to 11 bytes so you can safely store the byte-sized terminator.
.lcomm buffer, 11
.lcomm buffer2, 11
Then use this code:
_intToString:
mov $10, %ebx
add %ebx, %esi #Instead of 'ADD $10, %ESI' now that EBX==10
mov (terminator), %dl
mov %dl, (%esi)
.next_digit1:
xor %edx, %edx
div %ebx
add $0x30, %edx ##
dec %esi
mov %dl, (%esi)
test %eax, %eax
jnz .next_digit1
mov %esi, %eax
ret
The original NASM source uses
STRING_TERMINATOR equ 10
An equ does not consume memory at run-time. Your terminator: .byte 0 does use run-time memory! A good translation for the equ would be
.set terminator,0
Now you can write
_intToString:
mov $10, %ebx
add %ebx, %esi #Instead of 'ADD $10, %ESI' now that EBX==10
movb $terminator, (%esi)

How to write a swap function in assembly?

I've been trying to figure out how to write a x86 GAS swap function for my program. I know its easier to do xchg or just write it C, but I want to be able to write it out anyways.
On my 1st midterm we were given this as as swap function:
movl 8(%ebp), %edx
movl 12(%ebp), %ecx
movl (%edx), %ebx
movl (%ecx), %eax
movl %eax, (%edx)
movl %ebx, (%ecx)
but I receive a segmentation fault when running this. Haven't been able to succeed in finding my answer anywhere else on the web, so much help would be appreciated.
EDIT:
C CODE:
void program2()
{
int numA[2] = {5,10};
int *num1 = &numA[0];
int *num2 = &numA[1];
int loop=0;
printf("stop3\n");
for(loop=0;loop<=10;loop++)
{
*num1 *=2;
*num2 *=3;
printf("%d\n%d\n",*num1,*num2);
_asSwap(*num1,*num2);
printf("stop5\n");
printf("P2num1= %d\n P2num2= %d\n",*num1,*num2);
}
Assembly:
_asSwap:
push %ebp
movl %esp, %ebp
movl 8(%ebp), %edx
movl 12(%ebp), %ecx
movl (%edx), %ebx
movl (%ecx), %eax
movl %eax, (%edx)
movl %ebx, (%ecx)
pop %ebp
ret
You passed the values, not the pointers. If you'd included the prototype in your C, the compiler would have caught that (instead of warning about an un-declared function, and assuming it took int parameters).
extern int _asSwap(int *a, int *b);
A debugger would also have caught this, if you checked the address that segfaulted.
Also, it's not normal to prefix your C function names with _. OS X prefixes _ onto C symbol names, and so did Linux a.out (now replaced by ELF). So you in some cases need a leading _ in the asm, but don't use it in C.

Convert from ascii to integer in AT&T Assembly

How can I convert from ascii to integer?
data
asd:
.int 32
.text
.globl _start
_start:
movl $4, %eax
movl $1, %ebx
movl $1,(asd)
add $48,(asd)
movl $asd, %ecx
movl $4, %edx
int $0x80
# Exit the program
movl $1, %eax
movl $0, %ebx
int $0x80
The code is writing an ascii value, and I think that if I can add 48 to value. I can write, but I can't print, a two-stage number "for example 53 or 156". How can I print?
It's usually best to think about a high level language version first. If the number has n digits and is stored in array a, then we'd want:
char *p = a;
unsigned val = 0;
while (n > 0) {
n--;
val = 10 * val + (*p++ - '0');
}
So let's say %esi is p above %eax is val, and %ecx is n.
Translate line by line to keep it as simplistic and literal as possible:
movl $n, %ecx
movl $a, %esi
xorl %eax, %eax # val = 0
eval_while_cond:
testl %ecx, %ecx # if (n <= 0)
jle done # goto done
subl $1, %ecx # n--
movl %eax, %ebx # tmp1 = val
imul $10, %ebx # tmp1 = 10 * tmp1
movzbl (%esi), %eax # tmp2 = *p
addl $1, %esi # p++
subl $'0, %eax # tmp2 = tmp2 - '0'
addl %eax, %ebx # val = tmp2 + tmp1
jmp eval_while_cond
done:
# result is now in %eax
Note this is just a general methodology for getting functional assembly language. This isn't optimized in any way. The tricks needed to get optimal performance are a whole other topic.
The other options is to use a compiler (e.g. https://godbolt.org/z/WzGYjePzx) with light optimization like -O1, -Og, or -Os to get somewhat-readable asm (although in this case they use not-totally-obvious tricks with LEA to multiply by 10 and subtract '0' (48), or they make a mess with weird loop conditions.) See also How to remove "noise" from GCC/clang assembly output?

X86: making system calls without referring the standard library

I wrote the code that reads from stdin and writes to the stdout:
#include <stdio.h>
#include <unistd.h>
int main() /* copy input to output */
{
char buf[BUFSIZ];
int n;
while ((n = read(0, buf, BUFSIZ)) > 0)
write(1, buf, n);
return 0;
}
After I converted into the assembly code (a .s file) in 32-bit AT&T syntax:
.text
.globl _start
_start:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp #16 bit alignment
subl $8224, %esp #space for local variables
jmp _READ
_WRITE:
movl 8220(%esp), %eax
movl %eax, 8(%esp)
leal 28(%esp), %eax
movl %eax, 4(%esp)
movl $1, (%esp)
call write
int $0x80
_READ:
movl $8192, 8(%esp) #buffer length
leal 28(%esp), %eax
movl %eax, 4(%esp)
movl $0, (%esp)
call read
movl %eax, 8220(%esp)
cmpl $0, 8220(%esp)
jg _WRITE
movl $0, %eax
leave
ret
It works fine, but I'm not sure how to making the "read" and "write" system calls using plain assembly(i.e. moving numbers into certain registers and use "int 0x80" to execute the system calls).
My goal is to make it work even if it is compiled with the "-nostdlib" option.
Hint: 32-bit x86 is old, slow, weird and deprecated. You should use amd64 instead.
The list of system calls for Linux i386 is available in Linux source code:
https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscalls/syscall_32.tbl
Or in glibc headers in asm/unistd_32.h. You can and should #include <asm/unistd.h> so you can use $__NR_write instead of $4 to make your asm source code self-documenting.
The system call number goes in eax. Parameter sequence is always ebx, ecx, edx, esi, edi, ebp. So code becomes:
.text
.globl _start
_start:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp #16 bit alignment
subl $8224, %esp #space for local variables
jmp _READ
_WRITE:
movl 8220(%esp), %edx
leal 28(%esp), %ecx
movl $1, %ebx
movl $4, %eax
int $0x80
_READ:
movl $8192, %edx #buffer length
leal 28(%esp), %ecx
movl $0, %ebx
movl $3, %eax
int $0x80
movl %eax, 8220(%esp)
cmpl $0, 8220(%esp)
jg _WRITE
movl $1, %eax
movl $0, %ebx
int $0x80
Assemble and link with:
$ as --32 hel.s -o hel.o
$ ld -melf_i386 hel.o -o hel
http://www.linuxjournal.com/article/4048
See also
What are the calling conventions for UNIX & Linux system calls on i386 and x86-64
https://blog.packagecloud.io/eng/2016/04/05/the-definitive-guide-to-linux-system-calls/

What is the most efficient way to hardcode a map in Haskell?

I want to hard code a map in Haskell. I can see at least three ways to do it:
Using multiple equations:
message 200 = "OK"
message 404 = "Not found"
...
Using a case expression:
message s = case s of
200 -> "OK"
404 -> "Not found"
Actually using a Map.
Which is the most efficient way do it? Is one solution faster than the others, and why?
Are the first two solutions equivalent? (Will the compiler generates the same code?)
What is the recommended way (easier to read)?
(Note that I use Int in my example, but that is not essential. The keys might be Strings as well so I'm interested in both cases.)
Pattern matching on Ints happens in O(log(n)) time, like a map lookup.
Consider the following code, compiled to x86 assembly by ghc -S
module F (
f
) where
f :: Int -> String
f 0 = "Zero"
f 1 = "One"
f 2 = "Two"
f 3 = "Three"
f 4 = "Four"
f 5 = "Five"
f 6 = "Six"
f 7 = "Seven"
f _ = "Undefined"
The compiled assembly code is
.text
.align 4,0x90
.long _F_f_srt-(_sl8_info)+0
.long 0
.long 65568
_sl8_info:
.Lcma:
movl 3(%esi),%eax
cmpl $4,%eax
jl .Lcmq
cmpl $6,%eax
jl .Lcmi
cmpl $7,%eax
jl .Lcme
cmpl $7,%eax
jne .Lcmc
movl $_ghczmprim_GHCziCString_unpackCStringzh_closure,%esi
movl $_cm7_str,0(%ebp)
jmp _stg_ap_n_fast
.Lcmc:
movl $_ghczmprim_GHCziCString_unpackCStringzh_closure,%esi
movl $_clB_str,0(%ebp)
jmp _stg_ap_n_fast
.Lcme:
cmpl $6,%eax
jne .Lcmc
movl $_ghczmprim_GHCziCString_unpackCStringzh_closure,%esi
movl $_cm3_str,0(%ebp)
jmp _stg_ap_n_fast
.Lcmg:
cmpl $4,%eax
jne .Lcmc
movl $_ghczmprim_GHCziCString_unpackCStringzh_closure,%esi
movl $_clV_str,0(%ebp)
jmp _stg_ap_n_fast
.Lcmi:
cmpl $5,%eax
jl .Lcmg
cmpl $5,%eax
jne .Lcmc
movl $_ghczmprim_GHCziCString_unpackCStringzh_closure,%esi
movl $_clZ_str,0(%ebp)
jmp _stg_ap_n_fast
.Lcmk:
cmpl $2,%eax
jne .Lcmc
movl $_ghczmprim_GHCziCString_unpackCStringzh_closure,%esi
movl $_clN_str,0(%ebp)
jmp _stg_ap_n_fast
.Lcmm:
testl %eax,%eax
jne .Lcmc
movl $_ghczmprim_GHCziCString_unpackCStringzh_closure,%esi
movl $_clF_str,0(%ebp)
jmp _stg_ap_n_fast
.Lcmo:
cmpl $1,%eax
jl .Lcmm
cmpl $1,%eax
jne .Lcmc
movl $_ghczmprim_GHCziCString_unpackCStringzh_closure,%esi
movl $_clJ_str,0(%ebp)
jmp _stg_ap_n_fast
.Lcmq:
cmpl $2,%eax
jl .Lcmo
cmpl $3,%eax
jl .Lcmk
cmpl $3,%eax
jne .Lcmc
movl $_ghczmprim_GHCziCString_unpackCStringzh_closure,%esi
movl $_clR_str,0(%ebp)
jmp _stg_ap_n_fast
.text
.align 4,0x90
.long _F_f_srt-(_F_f_info)+0
.long 65541
.long 0
.long 65551
.globl _F_f_info
_F_f_info:
.Lcmu:
movl 0(%ebp),%esi
movl $_sl8_info,0(%ebp)
testl $3,%esi
jne .Lcmx
jmp *(%esi)
.Lcmx:
jmp _sl8_info
This is doing a binary search on the integer arguments. .Lcma is branching on <4 then <6 then <7. The first comparsion goes to .Lcmq which is branching on <2 then <3. The first comparison from that goes to .Lcmo, which branches on <1.
With ghc -O2 -S, instead we get this, where we can see the same pattern:
.text
.align 4,0x90
.long _F_zdwf_srt-(_F_zdwf_info)+0
.long 65540
.long 0
.long 33488911
.globl _F_zdwf_info
_F_zdwf_info:
.LcqO:
movl 0(%ebp),%eax
cmpl $4,%eax
jl .Lcr6
cmpl $6,%eax
jl .LcqY
cmpl $7,%eax
jl .LcqU
cmpl $7,%eax
jne .LcqS
movl $_F_f1_closure,%esi
addl $4,%ebp
andl $-4,%esi
jmp *(%esi)
.LcqS:
movl $_F_f9_closure,%esi
addl $4,%ebp
andl $-4,%esi
jmp *(%esi)
.LcqU:
cmpl $6,%eax
jne .LcqS
movl $_F_f2_closure,%esi
addl $4,%ebp
andl $-4,%esi
jmp *(%esi)
.LcqW:
cmpl $4,%eax
jne .LcqS
movl $_F_f4_closure,%esi
addl $4,%ebp
andl $-4,%esi
jmp *(%esi)
.LcqY:
cmpl $5,%eax
jl .LcqW
cmpl $5,%eax
jne .LcqS
movl $_F_f3_closure,%esi
addl $4,%ebp
andl $-4,%esi
jmp *(%esi)
.Lcr0:
cmpl $2,%eax
jne .LcqS
movl $_F_f6_closure,%esi
addl $4,%ebp
andl $-4,%esi
jmp *(%esi)
.Lcr2:
testl %eax,%eax
jne .LcqS
movl $_F_f8_closure,%esi
addl $4,%ebp
andl $-4,%esi
jmp *(%esi)
.Lcr4:
cmpl $1,%eax
jl .Lcr2
cmpl $1,%eax
jne .LcqS
movl $_F_f7_closure,%esi
addl $4,%ebp
andl $-4,%esi
jmp *(%esi)
.Lcr6:
cmpl $2,%eax
jl .Lcr4
cmpl $3,%eax
jl .Lcr0
cmpl $3,%eax
jne .LcqS
movl $_F_f5_closure,%esi
addl $4,%ebp
andl $-4,%esi
jmp *(%esi)
.section .data
.align 4
.align 1
_F_f_srt:
.long _F_zdwf_closure
.data
.align 4
.align 1
.globl _F_f_closure
_F_f_closure:
.long _F_f_info
.long 0
.text
.align 4,0x90
.long _F_f_srt-(_srh_info)+0
.long 0
.long 65568
_srh_info:
.Lcrv:
movl 3(%esi),%eax
movl %eax,0(%ebp)
jmp _F_zdwf_info
.text
.align 4,0x90
.long _F_f_srt-(_F_f_info)+0
.long 65541
.long 0
.long 65551
.globl _F_f_info
_F_f_info:
.Lcrz:
movl 0(%ebp),%esi
movl $_srh_info,0(%ebp)
testl $3,%esi
jne _srh_info
jmp *(%esi)
If we change the original code to
f :: Int -> String
f 1 = "Zero"
f 2 = "One"
f 3 = "Two"
f 4 = "Three"
f 5 = "Four"
f 6 = "Five"
f 7 = "Six"
f 8 = "Seven"
f _ = "Undefined"
The branches are on <5, <7, <8, with <5 going to <3, <4, etc., so it's probably doing this based on sorting the arguments. We can test that by scrambling the numbers, and even adding spacing between them:
f :: Int -> String
f 20 = "Zero"
f 80 = "One"
f 70 = "Two"
f 30 = "Three"
f 40 = "Four"
f 50 = "Five"
f 10 = "Six"
f 60 = "Seven"
f _ = "Undefined"
Sure enough, the branches are still on <50, <70, <80, with <50 going to <30, <40, etc.
Apparently function pattern matching happens in O(1) (constant time), while Map's lookup (of course referring to Data.Map) is guaranteed to be O(logn).
Considering the above assumptions, I'd go with pattern matching:
message 200 = "OK"
message 404 = "Not found"
The case ... of and the multiple equations are exactly equivalent. They compile to the same core. For a large number of cases you should probably do this:
import qualified Data.Map as Map
message =
let
theMap = Map.fromList [ (200, "OK"), (404, "Not found"), ... ]
in
\x -> Map.lookup x theMap
This constructs the map only once. If you don't like the Maybe String return type you can apply fromMaybe to the result.
For a small number of cases (especially if they are integers) the case statement is probably faster if the compiler can translate it to a jump table.
In an ideal world, ghc would pick the right version automatically.

Resources