3.3. Listas, Listas e mais Listas!

Nós explicamos variáveis e funções, e agora vamos adentrar os lodaçais turvos das listas de Scheme.

3.3.1. Definindo uma lista

Antes de falar mais sobre listas, é necessário que você entenda a diferença entre valores atômicos e listas.

Você já viu valores atômicos quando inicializamos variáveis na lição anterior. Um valor atômico é um único valor. Então, por exemplo, nós podemos atribuir para a variável x o valor simples de 42 como o comando seguinte:

(let* ( (x 8) ) x)

(Nós adicionamos a expressão x ao final da expressão para que o valor atribuído a x fosse impresso — normalmente você não precisa fazer isso. Note como o código do let* funciona quase como uma função: o valor da última expressão é o valor retornado.)

Uma variável também pode se referir a uma lista de valores, em vez de a um valor simples. Para atribuir a lista de valores 1, 3, 5 a uma variável x , nós digitaríamos:

(let* ( (x '(1 3 5))) x)

Digite ambos os comanos no Script-fu e veja como ele responde. Quando você digita o primeiro, ele simplesmente dá o resultado:

42

Entretanto, quando você digita o segundo comando, ele responde com o seguinte resultado:

(1 3 5)

Quando ele responde o valor 42, ele está informando você que a variável x contém o valor atômico 42. Entretanto, ao responder com (1 3 5), ele está informando você que a x não contém um único valor, mas uma lista de valores. Note que não há virgulas em nossa declaração ou atribuição de lista, nem no resultado impresso: os elementos são separados por espaço.

A sintaxe para definir uma lista é:

'(a b c)

Onde a, b, e c são valores literais. Nós usamos o apóstrofe (') para indicar que o que se segue entre parênteses é uma lista, e não uma chamada a função ou uma expressão.

Uma lista vazia pode ser definida assim:

'()

Ou simplesmente:

()

As listas podem conter valores atômicos, como também podem conter outras listas:

(let*
   (
        (x
           '("O GIMP" (1 2 3) ("é" ("demais" () ) ) )
        )
    )
    x
)
      

Note que após o primeiro apóstrofo, você não precisa mais usar um apóstrofo ao definir as listas internas. Vá em frente e copie a expressão acima no terminal do Script-fu (lembrando-se de digitar tudo numa linha só) e veja o que ele retorna.

Você deveria notar que o resultado que é retornado não é uma lista de valores atômicos simples. Em vez disso, é uma lista com um literal ("The GIMP"), a lista (1 2 3), e assim por diante.

3.3.2. Como pensar sobre as listas

É útil pensar nas listas como compostas de uma cabeça e uma cauda. A cabeça é o primeiro elemento da lista, e a cauda é o restante da lista. Você verá por que isso é importante quando nós discutirmos sobre como acrescentar elementos à listas e como acessar elementos dentro da lista.

3.3.3. Criando listas por concatenação (a função cons)

Uma das funções mais comuns que você vai encontrar é a função cons. Ela pega um valor, e coloca o mesmo no começo de seu segundo argumento, que deve ser uma lista. Na seção anterior, eu sugeri que você pensasse nas listas como sendo compostas de um elemento (a cabeça), seguida do restante da lista (a cauda). Isso é exatamente como a função cons funciona — ela adiciona um elemento como a cabeça de uma lista. Portanto, você pode criar uma lista dessa forma:

(cons 1 '(2 3 4) )

O resultado é a lista (1 2 3 4).

Você também pode criar uma lista com um único elemento:

(cons 1 () )

E, claro, você pode usar variáveis declaradas previamente no lugar de quaisquer literais, como seria de se esperar.

3.3.4. Definindo uma lista usando a função list

Para definir uma lista composta de literais ou de variáveis já declaradas, use a função list:

(list 5 4 3 a b c)

Isso irá compor e retornar uma lista contendo os valores das variáveis a, b e c. Por exemplo:

        (let*  (
                  (a 1)
                  (b 2)
                  (c 3)
               )

               (list 5 4 3 a b c)
        )
      

Esse código cria a lista (5 4 3 1 2 3).

3.3.5. Acessando valores em uma lista

Para acessar valores em uma lista, use as funções car e cdr, que retornam o primeiro elemento de uma lista (cabeça) e o restante da lista (cauda), respectivamente. Essas funções vão quebrar uma lista na construção cabeça::cauda, como eu mencionei anteriormente.

3.3.6. A função car

car returns the first element of the list (the head of the list). The list needs to be non-null (not empty). Thus, the following returns the first element of the list:

(car '("primeiro" 2 "terceiro"))

Que é

"primeiro"

3.3.7. A função cdr

cdr returns the remainder of the list after the first element (the tail of the list). If there is only one element in the list, it returns an empty list.

(cdr '("primeiro" 2 "terceiro"))

retorna:

(2 "terceiro")

Enquanto que o seguinte:

(cdr '("um e único"))

retorna:

()

3.3.8. Acessando outros elementos em uma lista

Ok, legal, nós temos o primeiro elemento de uma lista, e também o restante da lista, mas como acessamos o segundo, terceiro, ou outros elementos de uma lista? Existem várias funções de conveniência para acessar, por exemplo, a cabeça da cabeça da cauda de uma lista (caadr), a cauda da cauda de uma lista (cddr), e assim por diante.

A convenção de nomes usados é simples: os a's e d's representam as cabeças e caudas das listas, portanto:

(car (cdr (car x) ) )

Poderia ser escrito como:

(cadar x)

Para ganhar alguma prática em funções de acesso a elementos de lista, tente digitar o seguinte (lembre-se que tem que ser numa única linha); Use variações das funções car e cdr para acessar diferentes elementos da lista:

        (let* (
                 (x  '( (1 2 (3 4 5) 6)  7  8  (9 10) )
                 )
              )
              ; Coloque seu código com car/cdr aqui        )
      

Tente acessar o número 3 do conjunto acima usando somente duas chamadas as função. Se você puder fazer isso, você está no caminho certo para se tornar um Mestre de Script-fu!

[Nota] Nota

In Scheme, a semicolon (;) marks the beginning of a comment. It, and everything that follows it on the same line, are ignored by the script interpreter, so you can use this to add comments to refresh your memory when you look at the script later.