Combinações são entidades complexas feitas a partir de entidades mais simples. Por exemplo, na matemáticas números podem ser combinados usando operações como a soma ou o produto. Como exemplo de combinações matemáticas, temos 1 + 2 e 1 + 2 × 3. A soma e o produto de números são exemplos de operações extremamente elementares consideradas procedimentos primitivos.
Em Lisp, criam-se combinação escrevendo uma sequência de expressões entre um par de parênteses. Uma expressão é um elemento primitivo ou uma outra combinação. A expressão (+ 1 2) é uma combinação dos elementos primitivos 1 e 2 através do procedimento +. Já no caso (+ 1 (* 2 3)) a combinação é ocorre entre 1 e (* 2 3), sendo esta última expressão uma outra combinação.
Não é difícil de ver que as únicas combinações com utilidade são aquelas em que as expressões correspondem a operadores e operandos. Por convenção, o Lisp considera que o primeiro elemento de uma combinação é um operador e os restantes são os operandos.
A notação que o Lisp utiliza para construir expressões (operador primeiro e operandos a seguir) é designada por notação prefixa. Esta notação costuma causar alguma perplexidade a quem inicia o estudo da linguagem, que espera uma notação mais próxima da que aprendeu em aritmética e que é usada habitualmente nas outras linguagens de programação. Nestas, a expressão (+ 1 (* 2 3)) é usualmente escrita na forma 1 + 2 * 3 (designada notação infixa) que, normalmente, é mais simples de ler por um ser humano. No entanto, a notação prefixa usada pelo Lisp tem largas vantagens sobre a notação infixa:
> (+ 1 2 3) 6 > (+ 1 2 3 4 5 6 7 8 9 10) 55
Numa linguagem do tipo Pascal apenas existem operadores unários ou binários, e é necessário explicitar os operador binários entre cada dois operandos: 1+2+3 ou 1+2+3+4+5+6+7+8+9+10. Um operador deste tipo diz-se infixo (in significa entre). Se se pretender um operador ternário (ou outro) já não se consegue escrever do mesmo modo, sendo necessário implementá-lo como uma função.
Para exemplificar este último aspecto, consideremos a operação de exponenciação em Pascal. Para ser coerente com o resto da linguagem, deveria existir um operador, por exemplo ** que permitisse escrever 3**4 para indicar a quarta potência de 3. Como esse operador não existe na linguagem (standard), somos obrigados a criar uma função que o substitua mas, neste caso, a sintaxe muda radicalmente. Esta função, como se usa de forma prefixa não se pode tornar coerente com as restantes operações do Pascal, como a soma e a multiplicação. Em Lisp, pelo contrário, tanto podemos escrever (* 3 3 3 3) como definir uma função que permita escrever (** 3 4).
A desvantagem da notação prefixa está na escrita de combinações complexas. A expressão Pascal 1+2*3-4/5*6, equivale à expressão Lisp (- (+ 1 (* 2 3)) (* (/ 4 5) 6)) que embora seja mais simples de ler para uma máquina, pode ser mais difícil de ler para um ser humano devido à acumulação de parênteses. No entanto, esta expressão pode ser escrita de forma mais clara usando indentação.
A regra para indentação de combinações Lisp é extremamente simples. Numa linha coloca-se o operador e o primeiro operando. Os restantes operandos vêm alinhados por debaixo do primeiro.
(- (+ 1 (* 2 3)) (* (/ 4 5) 6))
Quando a regra de indentação não é suficiente, usam-se pequenas variações, como seja colocar o operador numa linha e os operandos por debaixo, por exemplo:
(umaoperacaocomumnomemuitogrande 1 2 3 4)
A indentação é fundamental em Lisp pois é muito fácil escrever código complexo. A grande maioria dos editores preparados para Lisp (Emacs, por exemplo) formatam automaticamente os programas à medida que os escrevemos e mostram o emparelhamento de parênteses. Desta forma, após algum tempo de prática, torna-se muito fácil escrever e ler os programas, por mais complexa que possa parecer a sua estrutura.
Exercício 3
Converta as seguintes expressões da notação infixa da aritmética para a notação prefixa do Lisp:
1 + 2 - 3 1 - 2 * 3 1 * 2 - 3 1 * 2 * 3 (1 - 2) * 3 (1 - 2) + 3 1 - (2 + 3) 2 * 2 + 3 * 3 * 3
Exercício 4
Converta as seguintes expressões da notação prefixa do Lisp para a notação infixa da aritmética:
(* (/ 1 2) 3) (* 1 (- 2 3)) (/ (+ 1 2) 3) (/ (/ 1 2) 3) (/ 1 (/ 2 3)) (- (- 1 2) 3) (- 1 2 3)