Strings

Revisão de strings

Ao longo dos primeiros capítulos desse livro nós usamos strings para representar palavras ou frases que desejávamos imprimir. Nossa definição foi simples. Um string é simplesmente um grupo de caracteres entre aspas. Nesse capítulo vamos explorar strings com muito mais detalhes.

Um tipo de dado composto

Até aqui vimos tipos de dados nativos como: int, float, bool, str e vimos também listas. Os tipos int, float e bool são considerados tipos simples ou primitivos pois seus valores não são compostos por outras partes menores. Eles não podem ser quebrados. Por outro lado, strings e listas são qualitativamente diferentes desses tipos pois eles são construídos de pedaços menores. No caso de strings, eles são construídos de strings menores, cada um contendo um caractere.

Tipos que são compostos de pedaços menores são chamados tipos de dados compostos. Dependendo do que estamos fazendo, a gente pode querer tratar um tipo composto como uma entidade única (o todo), ou a gente pode querer acessar suas partes. Essa ambiguidade é útil.

Strings podem ser definidos como coleções de sequencias de caracteres. Isto significa que assumimos que os caracteres individuais que compõem a cadeia estão em uma determinada ordem, da esquerda para a direita.

Uma cadeia que não possui nenhum caractere, muitas vezes chamada de string vazio, é também considerado um string. É simplesmente uma sequência com zero caracteres e é representado por ‘’ ou “” (dois apóstrofes ou aspas sem nada entre eles – observe que o string com um ou mais caracteres em branco como ‘ ‘ é diferente do string vazio ‘’).

Operações com strings

Em geral, você não pode executar operações matemáticas com strings, mesmo que os strings se pareçam com números. As seguintes operações são ilegais (assumindo que `` message`` seja um string):

message - 1
"Hello" / 123
message * "Hello"
"15" + 2

Curiosamente, o operador + funciona com strings. No entanto, esse o operador + representa concatenação e não adição. Concatenação significa juntar os dois operandos unindo o fim de um com o começo do outro. Por exemplo:

A saída deste programa é bolo de banana. O espaço antes da palavra `` banana`` faz parte da cadeia, e é necessário para produzir o espaço entre os strings concatenados. Retire o espaço e execute o programa novamente.

O operador * também funciona com strings; ele realiza repetição. Por exemplo, 'Vai'*3 é 'VaiVaiVai'. Um dos operadores tem que ser um string; o outro tem de ser um número inteiro.

Esta interpretação de + e * faz sentido por analogia com adição e multiplicação. Assim como 4*3 é equivalente a 4+4+4, nós esperamos que "Vai "*3 a ser o mesmo que "Vai "+"Vai "+" Vai ", e é. Note-se também no último exemplo, que a ordem de operações para * e + é o mesmo que foi para a aritmética. A repetição é feita antes da concatenação. Se você quer fazer com que a concatenação seja feita primeiro, você vai precisar usar parênteses.

Teste seu entendimento

    Q-3: O que é impresso pelos seguintes comandos?

    s = "python"
    t = "rocks"
    print(s+t)
    
  • python rocks

  • A concatenação não adiciona espaços.

  • python

  • A expressão s+t é calculada primeiro, então o resultado é impresso

  • pythonrocks

  • Sim, os dois strings são unidos.

  • Erro, você não pode somar duas strings.

  • O operador + tem significado diferente dependendo de seus operandos, nesse caso, dois strings.

    Q-4: O que é impresso pelos seguintes comandos?

    s = "python"
    excl = "!"
    print(s+excl*3)
    
  • python!!!

  • Sim, repetição tem precedência sobre concatenação.

  • python!python!python!

  • Repetição é feita primeiro.

  • pythonpythonpython!

  • O operador de repetição está trabalhando sobre a variável excl.

  • Erro, você não pode fazer concatenação e repetição ao mesmo tempo.

  • Os operadores + e * são definidos tanto para strings quanto para números.

Operador de indexação

O operador de indexação (o Python usa colchetes para incluir o índice) seleciona um único caractere de um string. Os caracteres são acessados por sua posição ou valor do índice. Por exemplo, na sequência mostrada abaixo, os 14 caracteres são indexados da esquerda para a direita a partir posição 0 até a posição 13.

index values

As posições podem ser acessadas também da direita para a esquerda usando números negativos, onde -1 é o índice mais à direita e assim por diante. Note-se que o caractere no índice 6 (ou -8) é o caractere em branco.

A expressão escola[2] seleciona o caractere no índice 2 de escola, e cria um novo string contendo apenas este caractere. A variável m faz referência ao resultado.

Lembre-se que os cientistas da computação muitas vezes começam a contagem do zero. A letra no índice zero do "Carlos Gomes" é C. Então, na posição [2] temos a letra r.

Se você quiser a letra de índice zero de um string, simplesmente coloque 0, ou qualquer expressão com o valor 0, dentro dos colchetes. Experimente.

A expressão entre parênteses é chamada de índice. Um índice especifica um membro de uma coleção ordenada. Neste caso, a coleção de caracteres na sequência. o índice indica qual caractere você quer. Pode ser qualquer expressão inteira desde que ela seja avaliada como um valor de índice válido.

Observe que a indexação retorna um string — o Python não tem nenhum tipo especial para um único caractere. É apenas uma sequência de comprimento 1.

Teste seu entendimento

    Q-6: O que é impresso pelos seguintes comandos?

    s = "python rocks"
    print(s[3])
    
  • t

  • Os índices não começam de 1, eles começam de 0.

  • h

  • Sim, os índices começam de 0.

  • c

  • s[-3] retorna c, contando da direita para a esquerda.

  • Erro, você não pode usar o operador [ ] com um string.

  • [ ] é o operador de índice.

    Q-7: O que é impresso pelos seguintes comandos?

    s = "python rocks"
    print(s[2] + s[-5])
    
  • tr

  • Sim, o operador de indexação tem precedência sobre concatenação.

  • ps

  • p está na posição 0, não 2.

  • nn

  • n está na posição 5, não 2.

  • Erro, você não pode usar o operador [ ] com o operador +.

  • o operador [ ] retorna um string que pode ser concatenado com outro string.

Métodos de string

Nós já vimos que cada instância de tartaruga tem seus próprios atributos e um número de métodos que podem ser aplicados à instância. Por exemplo, nós escrevemos tess.right(90) para que o objeto tartaruga tess execute o método right para virar 90 graus para a direita. A “notação de ponto” é a forma de ligar o nome de um objeto com o nome de um método que ele pode executar.

Strings também são objetos. Cada instância de string tem seus próprios atributos e métodos. O atributo mais importante do string é a coleção de caracteres. Há uma grande variedade de métodos. Rode o seguinte programa.

Neste exemplo, upper é um método que pode ser chamado em qualquer objeto string para criar um novo string no qual todos os caracteres estão em maiúsculas. lower funciona de forma semelhante, onde todos os caracteres do novo string estão em minúsculas. (O string original ss permanece inalterado. Um novo string é criado e é referenciado por tt.)

Além de upper e lower, a tabela a seguir apresenta um resumo de alguns outros métodos úteis de string. Existem alguns exemplos em activecode que se seguem, de modo que você pode experimentá-los.

Método

Parâmetros

Descrição

upper

nenhum

Retorna um string todo em maiúsculas

lower

nenhum

Retorna um string todo em minúsculas

capitalize

nenhum

Retorna um string com o primeiro caractere em maiúscula, e o resto em minúsculas

strip

nenhum

Retorna um string removendo caracteres em branco do início e do fim

lstrip

nenhum

Retorna um string removendo caracteres em brando do início

rstrip

nenhum

Retorna um string removendo caracteres em brando do fim

count

item

Retorna o número de ocorrências de item

replace

old, new

Substitui todas as ocorrências do substring old por new

center

largura

Retorna um string centrado em um campo de tamanho largura

ljust

largura

Retorna um string justificado à esquerda em um campo de tamanho largura

rjust

largura

Retorna um string justificado à direita em um campo de tamanho largura

find

item

Retorna o índice mais à esquerda onde o substring item é encontrado

rfind

item

Retorna o índice mais à direita onde o substring item é encontrado

index

item

Como find, mas causa um erro em tempo de execução caso item não seja encontrado

rindex

item

Como rfind, mas causa um erro em tempo de execução caso item não seja encontrado

Você deve experimentar esses métodos para que você entenda o que eles fazem. Observe que os métodos que retornam um string não alteram o original. Você também pode consultar a documentação do Python para strings.

Teste seu entendimento

    Q-11: O que é impresso pelos seguintes comandos?

    s = "python rocks"
    print(s.count("o") + s.count("p"))
    
  • 0

  • Com certeza temos alguns caracteres o e p.

  • 2

  • Há 2 caracteres o mas quantos são p?

  • 3

  • Sim, some o número de caracteres o com o número de caracteres p.

    Q-12: O que é impresso pelos seguintes comandos?

    s = "python rocks"
    print(s[1]*s.index("n"))
    
  • yyyyy

  • Sim, s[1] é y e o índice de n é 5, portanto 5 caracteres y.

  • 55555

  • Quase. 5 não é repetido, é o número de vezes a serem repetidas.

  • n

  • Essa expressão usa o índice de n.

  • Error, you cannot combine all those things together.

  • Não há erro, o operador de repetição * usou o resultado de indexação e o método index.

Comprimento

A função len, quando aplicada a um string, retorna o número de caracteres no string (ou seja, o seu comprimento).

Para pegar o último caractere de um string, você pode ficar tentado a experimentar algo como:

Isso não vai funcionar. Isso causa o erro em tempo de execução IndexError: string index out of range. A razão é que não há nenhum caractere na posição de índice 6 em "Banana". Como começamos a contar do zero, os seis índices são numeradas de 0 a 5. Para pegar o último caractere, temos que subtrair 1 comprimento. Experimente, tente executar o exemplo acima.

Normalmente, um programador Python irá acessar o último caractere combinando as duas linhas de código acima.

lastch = fruta[len(fruta)-1]

Teste seu entendimento

    Q-16: O que é impresso pelos seguintes comandos?

    s = "python rocks"
    print(len(s))
    
  • 11

  • O espaço em branco conta como um caractere.

  • 12

  • Sim, há 12 caracteres no string.

    Q-17: O que é impresso pelos seguintes comandos?

    s = "python rocks"
    print(s[len(s)-5])
    
  • o

  • Dê uma nova olhada no cálculo do índice, len(s)-5

  • r

  • Sim, len(s) é 12 e 12-5 é 7. Use 7 como índice e lembre-se de começar a contar do 0.

  • s

  • O s está no índice 11.

  • Erro, len(s) é 12 e o índice 12 não existe.

  • Você deve subtrair 5 antes de usar o operador índice.

Fatiamento

Um substring de um string é chamado de fatia (do inglês slice). Selecionar uma fatia é semelhante a selecionar um caractere:

O operador de fatia [n:m] retorna a parte do string a partir do n-ésimo caractere até o m-ésimo caractere, incluindo o primeiro mas excluindo o último. Em outras palavras, comece com o caractere no índice n e vá até, mas não inclua, o caractere no índice m. Esse comportamento pode parecer contra-intuitivo, mas se você se lembra a função range, ela não inclui o seu ponto final também.

Se você omitir o primeiro índice (antes dos dois pontos), a fatia começa no início da cadeia. Se você omitir o segundo índice, a fatia vai até o fim da cadeia.

O que você acha que fruta[:] significa?

Teste seu entendimento

    Q-20: O que é impresso pelos seguintes comandos?

    s = "python rocks"
    print(s[3:8])
    
  • python

  • Isso seria s[0:6].

  • rocks

  • Isso seria s[7:].

  • hon r

  • Sim, comece com o caractere no índice 3 e vá até, mas sem incluir, o caractere de índice 8.

  • Erro, você não pode ter dois números dentro do [ ].

  • Isso é chamado de fatiamento, não indexação. O fatiamento requer um início e um fim.

    Q-21: O que é impresso pelos seguintes comandos?

    s = "python rocks"
    print(s[7:11]*3)
    
  • rockrockrock

  • Sim, rock começa em 7 e vai até 10. Repita 3 vezes.

  • rock rock rock

  • Repetição não insere um espaço.

  • rocksrocksrocks

  • Fatiamento não inclui o caractere de índice 11, apenas até o caractere anterior (10 nesse caso).

  • Erro, você não pode usar repetição com fatiamento.

  • O fatiamento ocorre primeiro e depois a repetição. Assim não há erro.

index:: comparação de strings

Comparação de strings

Os operadores de comparação também funcionam com strings. Para ver se dois strings são iguais, basta escrever uma expressão booleana usando o operador de igualdade.

Outras operações de comparação são úteis para colocar palavras em ordem lexicográfica <http://en.wikipedia.org/wiki/Lexicographic_order> __. Isto é semelhante à ordem alfabética usada em um dicionário, exceto que todas as letras maiúsculas vêm antes de todas as letras minúsculas.

Deve ser evidente para você que a palavra apple seria inferior a (deve vir antes) palavra` banana`. Afinal, a vem antes de b no alfabeto. Mas e se considerarmos as palavras apple e Apple? Eles são iguais?

Acontece que, como você deve se lembrar da nossa discussão sobre nomes de variáveis, que letras maiúsculas e minúsculas são consideradas diferentes umas das outras. A forma como o computador sabe que eles são diferentes é que cada caractere corresponde a um valor inteiro único. Por exemplo, “A” é de 65, “B” é de 66, e “5” é 53. Você pode descobrir o chamado valor ordinal de um caractere utilizando a função ord.

Quando você compara caracteres ou strings, o Python converte os caracteres para seus valores ordinais e compara os inteiros da esquerda para a direita. Como você pode ver no exemplo acima, “a” é maior que “A” de forma que “apple” é maior do que “Apple”.

Nós geralmente ignoramos a capitalização ao comparar duas palavras. No entanto, os computadores não. Uma maneira comum de resolver este problema é converter as strings para um formato padrão, tal como todas as letras minúsculas, antes de realizar a comparação.

Há também uma função semelhante chamada chr que converte inteiros para o seu caractere equivalente.

Uma coisa a notar nos dois últimos exemplos é o fato de que o caractere de espaço tem um valor ordinal (32). Mesmo que você não o veja, ele é um caractere real. Às vezes chamamos esses caracteres de não impressos.

Teste seu entendimento

    Q-27: Qual o resultado da seguinte comparação:

    "mel" < "melhor"
    
  • True

  • Ambos os strings são idênticos até o l mas como mel é mais curto que melhor então ele vem primeiro no dicionário.

  • False

  • Strings são comparados caractere a caractere.

    Q-28: Qual o resultado da seguinte comparação:

    "mel" < "Mel"
    
  • True

  • m é maior que M de acordo com a função ord (77 versus 109).

  • False

  • Sim, um caractere maiúsculo é menor que seu correspondente minúsculo de acordo com os seus valores ordinais.

  • Eles são a mesma palavra

  • Em Python, letras maiúsculas e minúsculas são diferentes.

    Q-29: Qual o resultado da seguinte comparação:

    "mel" < "Melhor"
    
  • True

  • m é maior que M.

  • False

  • O comprimento não é importante.

Strings são imutáveis

Uma última coisa que faz que strings sejam diferentes de alguns outros tipos coletivos de Python é que você não tem permissão para modificar os caracteres individuais na coleção. É tentador usar o operador [] no lado esquerdo de uma atribuição com a intenção de mudar um caractere em um string. Por exemplo, no código a seguir, gostaríamos de mudar a primeira letra de conversa.

Em vez de produzir a saída Bla, mundo, este código produz o erro de execução TypeError: 'str' object does not support item assignment.

Strings são imutáveis, o que significa que você não pode mudar um string existente. O melhor que você pode fazer é criar um novo string que é uma variação do original.

A solução aqui é concatenar uma nova primeira letra com uma fatia de conversa. Esta operação não tem efeito sobre o string original.

Teste seu entendimento

    Q-32: O que é impresso pelos seguintes comandos?

    s = "Ball"
    s[0] = "C"
    print(s)
    
  • Ball

  • Atribuição não é permitida com strings.

  • Call

  • Atribuição não é permitida com strings.

  • Error

  • Sim, strings são imutáveis.

Varredura com for por item

Um grande número de computações envolvem o processamento de item de um conjunto de cada vez. Para strings, isto significa que nós gostaríamos de processar um caractere de cada vez. Muitas vezes, nós começamos no início, selecionamos um caractere de cada vez, fazemos alguma coisa com ele, e continuamos até o final. Este padrão de processamento é chamado um percurso.

Anteriormente, vimos que o comando for pode iterar sobre os itens de uma sequência (uma lista de nomes no caso abaixo).

Lembre-se que a variável do laço assume cada valor da sequência de nomes. O corpo é executado uma vez para cada nome. O mesmo era verdade para a sequência de números inteiros criada pela função range.

Como um string é simplesmente uma sequência de caracteres, o laço for itera sobre cada caractere automaticamente.

A variável um_char do laço recebe automaticamente cada caractere da sequência “Venha para a festa”. Vamos nos referir a esse tipo de iteração de sequência como iteração por item. Note que só é possível processar os caracteres um de cada vez, da esquerda para a direita.

Teste seu entendimento

    Q-36: Quantas vezes a palavra OLA é impressa pelos seguintes comandos?

    s = "viva o python"
    for ch in s:
        print("OLA")
    
  • 11

  • Iteração por item processará cada item da sequência uma vez.

  • 12

  • Os espaços fazem parte da sequência.

  • 13

  • Sim, há 13 caracteres, incluindo os espaços.

  • Erro, o comando for precisa ser usado com a função range.

  • O comando for pode iterar sobre uma sequência item por item.

    Q-37: Quantas vezes a palavra OLA é impressa pelos seguintes comandos?

    s = "viva o python"
    for ch in s[3:8]:
        print("OLA")
    
  • 4

  • A fatia retorna uma sequência que pode ser percorrida. Os espaços fazem parte da sequência.

  • 5

  • Sim, os espaços fazem parte da sequência retornada pela fatia.

  • 6

  • Verifique o resultado de s[3:8]. Ele não inclui o item de índice 8.

  • Erro, o comando for não pode usar fatias.

  • A fatia s[3:8] retorna uma sequência.

Varredura com for por índice

Também é possível utilizar a função range para gerar sistematicamente os índices dos caracteres. O laço for pode então ser usado para iterar sobre essas posições. Estas posições podem ser utilizados em conjunto com o operador de indexação para acessar os caracteres individuais na sequência.

Considere o seguinte exemplo no codelens.

CodeLens: (ch08_7)

As posições de índice de “apple” são 0,1,2,3 e 4. Esta é exatamente a mesma sequência de números inteiros retornado por range(5). Na primeira vez dentro do laço for, idx será 0 e o “a” será impresso. Em seguida, idx receberá 1 e “p” será exibido. Isso vai se repetir para todos os valores do intervalo até, mas não incluindo, o 5. Como “e” tem índice 4, este programa vai mostrar todos os caracteres de “apple”.

A fim de tornar a iteração mais genérica, podemos usar a função len para fornecer o limite para range. Este é um padrão muito comum para percorrer qualquer sequência por posição. Certifique-se de entender por que a função range se comporta corretamente ao usar len de um string como parâmetro.

Você também pode notar que a iteração por posição permite que o programador controle a direção da travessia, alterando a sequência de valores dos índices. Lembre-se que nós podemos criar tanto intervalos que contam para baixo quanto para cima. Assim o código a seguir irá imprimir os caracteres da direita para a esquerda.

CodeLens: (ch08_8)

Simule os valores de idx e verifique que eles estão corretos. Em particular, note o início e o fim do intervalo.

Teste seu entendimento

    Q-41: Quantas vezes a letra o é impressa pelos seguintes comandos?

    s = "python rocks"
    for idx in range(len(s)):
        if idx % 2 == 0:
            print(s[idx])
    
  • 0

  • O laço for visita todos os índices mas a seleção somente imprime alguns.

  • 1

  • o está nas posições 4 e 8.

  • 2

  • Sim, todos os caracteres nas posições pares serão impressos.

  • Erro, o comando for não pode conter um comando if.

  • O comando for pode conter qualquer outro comando, incluindo if e outros comandos for.

Varredura com while

O laço while também pode controlar a geração dos valores de índices. Lembre-se que o programador é responsável por configurar a condição inicial, certificando-se que a condição é correta e certificando-se de que algo muda dentro do corpo para garantir que a condição se tornará falsa.

A condição do loop é position < len(fruta), por isso, quando position é igual ao comprimento do string, a condição é falsa, e o corpo do laço não é executado. O último caractere acessado é aquele com o índice len(fruta)-1, que é o último caractere no string.

Aqui está o mesmo exemplo em codelens para que você possa verificar os valores das variáveis.

CodeLens: (ch08_7c1)

Teste seu entendimento

    Q-44: Quantas vezes a letra o é impressa pelos seguintes comandos?

    s = "python rocks"
    idx = 1
    while idx < len(s):
        print(s[idx])
        idx = idx + 2
    
  • 0

  • Sim, idx varre os números ímpares a partir do 1 e a letra o está nas posições 4 e 8.

  • 1

  • a letra o está nas posições 4 e 8, e o idx começa em 1, não 0.

  • 2

  • Há duas letras o mas idx não recebe os valores de índice corretos.

Operadores in e not in

O operador in testa se um string é um substring de outro:

Note que um string é um substring de si mesmo, e o string vazio é um substring de qualquer outro string. (Note também que os cientistas da computação gostam de pensar sobre estes casos particulares com muito cuidado!)

O operador not in retorna o resultado lógico oposto de in.

Padrão de acumulação com strings

Combinando o operador in com concatenação de strings usando + e o padrão de acumulação, podemos escrever uma função que remove todas as vogais de um string. A idéia é começar com um string e iterar sobre cada caractere, verificando se o caractere é uma vogal. À medida que processamos os caracteres, vamos construindo uma nova cadeia contendo apenas os caracteres não vogais. Para fazer isso, usamos o padrão de acumulação.

Lembre-se que o padrão de acumulação nos permite manter uma “total em execução”. Com strings, não estamos acumulando um total numérico. Em vez disso, estamos acumulando caracteres em um string.

A linha 5 usa o operador not in para verificar se o caractere atual não se encontra no string vowels. A alternativa para o uso deste operador seria escrever uma grande declaração if que verifica cada vogal individualmente. Note que precisaríamos usar operadores lógicos and para ter certeza de que o caractere não é uma vogal.

if eachChar != 'a'  and eachChar != 'e'  and eachChar != 'i'  and
   eachChar != 'o'  and eachChar != 'u'  and eachChar != 'A'  and
   eachChar != 'E'  and eachChar != 'I'  and eachChar != 'O'  and
   eachChar != 'U':

     sWithoutVowels = sWithoutVowels + eachChar

Olhe atentamente para a linha 6 no programa acima (sWithoutVowels = sWithoutVowels + eachChar). Vamos fazer isso para cada caractere que não é uma vogal. Isso deve parecer muito familiar. Como descremos anteriormente, este é um exemplo do padrão de acumulação, desta vez usando um string para “acumular” o resultado final. Em palavras, ele diz que o novo valor de sWithoutVowels será o valor antigo de sWithoutVowels concatenado com o valor de eachChar. Estamos construindo o string resultante caractere por caractere.

Dê uma olhada também na inicialização de sWithoutVowels. Começamos com um string vazio e, em seguida, adicionamos novos caracteres até o fim.

Simule a execução da função usando codelens para ver a variável acumuladora crescer.

CodeLens: (ch08_acc2)

Teste seu entendimento

    Q-50: O que é impresso pelos seguintes comandos:

    s = "ball"
    r = ""
    for item in s:
        r = item.upper() + r
    print(r)
    
  • Ball

  • Cada item é convertido para maiúscula antes da concatenação.

  • BALL

  • Cada caractere é convertido para maiúscula mas a ordem está errada.

  • LLAB

  • Sim, a ordem é reversa devido a ordem da concatenação.

Tartarugas, strings e Sistemas-L

Esta seção descreve um exemplo muito mais interessante de iteração de strings e o padrão de acumulação. Mesmo que pareça que estamos fazendo algo que é muito mais complexo, o processamento básico é o mesmo que foi mostrado nas seções anteriores.

Em 1968 Astrid Lindenmayer, uma bióloga, inventou um sistema formal que fornece uma descrição matemática do crescimento de plantas conhecido como um sistema-L. O sistema-L foi concebido para modelar o crescimento de sistemas biológicos. Você pode considerar que um sistema-L contem as instruções de como uma única célula pode se transformar em um organismo complexo. Sistemas-L podem ser utilizados para especificar as regras para todos os tipos de padrões interessantes. No nosso caso, vamos usá-los para especificar as regras para desenhar figuras.

As regras de um sistema-L são na verdade um conjunto de instruções para transformar um string em um novo string. Depois de uma série de tais transformações de strings se completar, o string contém um conjunto de instruções. Nosso plano é deixar estas instruções dirigirem uma tartaruga para desenhar uma figura.

Para começar, vamos ver um exemplo de conjunto de regras:

A

Axioma

A -> B

Regra 1 Mude A para B

B -> AB

Regra 2 Mude B to AB

Cada conjunto de regras contém um axioma que representa o estado inicial do processo de transformação. As regras são da forma:

lado esquerdo -> lado direito

Onde o lado esquerdo é um único símbolo e o lado direito é uma sequência de símbolos. Você pode considerar ambos os lados como strings. A forma de usar as regras é substituir as ocorrências do lado esquerdo pelos correspondentes lados direitos.

Agora vamos dar uma olhada nessas regras simples em ação, começando a partir do string A:

A
B      Aplique a Regra 1  (A é substituido por B)
AB     Aplique a Regra 2  (B é substituido por AB)
BAB    Aplique a Regra 1 para A e então a Regra 2 para B
ABBAB  Aplique a Regra 2 para B, Regra 1 para A, e Regra 2 para B

Observe que cada linha representa uma nova transformação para todo o string. Cada caractere do string original que corresponda a um do lado esquerdo de uma regra foi substituído pelo lado direito correspondente da mesma regra. Depois de fazer a substituição para cada caractere no string original, obtemos o string transformado.

Então como podemos codificar essas regras em um programa Python? Há algumas coisas muito importantes a serem observadas aqui:

  1. As regras são muito parecidas com comandos if.

  2. Vamos começar com um string e iterar sobre cada um de seus caracteres.

  3. À medida que aplicamos as regras para um string vamos criando um string novo usando o padrão de acumulação. Quando todos os caracteres do string original forem processados, nós o substituímos pelo novo string transformado.

Vamos dar uma olhada em um programa simples em Python que implementa o exemplo de conjunto de regras descrito acima.

Tente executar o exemplo acima, com valores diferentes para o parâmetro numIters. Você verá que, para os valores 1, 2, 3 e 4, os strings gerados são idênticos aos do exemplo acima.

Uma das coisas legais sobre o programa acima é que se você quiser implementar um conjunto diferente de regras, você não precisa re-escrever o programa inteiro. Tudo que você precisa fazer é re-escrever a função applyRules.

Suponha que você tenha as seguintes regras:

A

Axioma

A -> BAB

Regra 1 Mude A para BAB

Que tipo de string seria criado por essa regra? Modifique o programa acima para implementar essa regra.

Agora vamos dar uma olhada em um sistema-L real que implementa um desenho famoso. Esse sistema-L possui apenas duas regras:

F

Axioma

F -> F-F++F-F

Regra 1

Esse sistema-L usa símbolos que terão significado especial quando usados para a tartaruga desenhar uma figura.

F

Dê alguns passos para a frente

B

Dê alguns passos para trás

-

Vire alguns graus à esquerda

+

Vire alguns graus à direita

Essa é a função applyRules para esse sistema-L.

def applyRules(ch):
    newstr = ""
    if ch == 'F':
        newstr = 'F-F++F-F'   # Rule 1
    else:
        newstr = ch    # no rules apply so keep the character

    return newstr

Muito simples até agora. Como você pode imaginar esse string vai ficar muito longo com algumas aplicações das regras. Você pode tentar expandir a cadeia algumas vezes no papel só para ver.

O último passo é pegar o string final e transformá-lo em uma figura. Vamos assumir que a tartaruga sempre dá 5 passos para frente ou para trás. Além disso, vamos assumir que a tartaruga sempre gira 60 graus ao virar para a direita ou esquerda. Observe agora o string F-F++F-F. Você pode tentar seguir a explicação acima para ver a figura que este string simples representa. Neste ponto a figura não é grande coisa, mas depois de expandir o string algumas vezes ele vai ficar muito mais interessante.

Para criar uma função em Python para desenhar um string vamos escrever uma função chamada drawLsystem. A função terá quatro parâmetros:

  • Uma tartaruga para fazer o desenho

  • Um string expandido que contém o resultados da expansão usando as regras acima.

  • Um ângulo para virar

  • Uma distância para avançar ou retroceder

    def drawLsystem(aTurtle,instructions,angle,distance):
        for cmd in instructions:
            if cmd == 'F':
                aTurtle.forward(distance)
            elif cmd == 'B':
                aTurtle.backward(distance)
            elif cmd == '+':
                aTurtle.right(angle)
            elif cmd == '-':
                aTurtle.left(angle)
            else:
                print('Error:', cmd, 'is an unknown command')
    

Esse é o programa completo em activecode. A função main primeiro cria o string do sistema-L e então cria a tartaruga e passa a tartaruga e o string para a função de desenho.

Sinta-se a vontade para tentar alguns ângulos e distâncias diferentes para ver como a figura se modifica.

Repetições e contagens

Vamos terminar este capítulo com mais alguns exemplos que mostram variações sobre o tema da iteração pelos dos caracteres de um string. Vamos implementar alguns dos métodos que descrevemos anteriormente para mostrar como eles podem vir a ser.

O programa a seguir conta o número de vezes que uma letra em particular, aChar, aparece em um string. É mais um exemplo do padrão de acumulação que vimos nos capítulos anteriores.

A função count recebe um string como parâmetro. O comando for itera por cada caractere do string e verifica se o caractere é igual ao valor de aChar. Se igual, a variável de contagem, lettercount, é incrementada. Quando todos os caracteres forem processados, lettercount é retornada.

Função find

Essa é uma implementação da função find, que procura um caractere em um string.

Num certo sentido, find é o oposto do operador de indexação. Em vez de receber um índice e extrair o caractere correspondente, ela pega um caractere e encontra o índice onde esse caractere aparece pela primeira vez. Se o caractere não for encontrado, a função retorna -1.

O laço while neste exemplo usa uma condição um pouco mais complexa do que temos visto em programas anteriores. Aqui há duas partes para a condição. Queremos continuar se houver mais caracteres para serem olhados e queremos continuar caso ainda não tenhamos encontrado o que estamos procurando. A variável found é uma variável booleana que controla se já encontramos o caractere que estamos procurando. Ele é inicializado com False. Se encontrarmos o caractere, found recebe True.

A outra parte da condição é a mesma que foi utilizada anteriormente para percorrer os caracteres do string. Como combinamos essas duas partes com um operador lógico and, é necessário que ambas as partes sejam True para continuar a iteração. Se uma parte falhar, a condição falha e a iteração pára.

Quando a iteração pára, nós simplesmente fazemos uma pergunta para descobrir o porquê e, em seguida, retornamos o valor adequado.

Note

Esse padrão de computação é chamado às vezes de percurso eureka pois assim que encontramos o que estamos procurando podemos gritar Eureka! e parar de procurar. A forma de parar de procurar é alterando o valor de found para True, que causa a falha na condição.

Parâmetros opcionais

Para encontrar a posição da segunda ou terceira ocorrência de um caractere em um string nós podemos modificar a função find, adicionando um terceiro parâmetro para a posição inicial no string de busca:

A chamada find2('banana', 'a', 2) agora retorna 3, o índice da primeira ocorrência de ‘a’ em ‘banana’ depois do índice 2. O que é retornado pela chamada find2('banana', 'n', 3)? Se você respondeu 4, há uma boa chance que você tenha entendido como find2 funciona. Experimente.

Melhor ainda, podemos combinar find e find2 usando um parâmetro opcional.

A chamada find3('banana', 'a', 2) para essa versão de find se comporta da mesma forma que find2, enquanto na chamada find3('banana', 'a'), start receberá o valor default de 0.

Adicionando um outro parâmetro opcional em find permite que a busca seja feita a partir de uma posição inicial (start), até mas não incluindo a posição final (end).

O valor opcional para end é interessante. Nós damos um valor default None se o chamador não fornecer esse argumento. No corpo da função testamos o valor de end e, caso seja None, vamos atribuir o comprimento do string para end. No entanto, se o chamador tiver fornecido um argumento para end, o valor do chamador será utilizado no laço.

A semântica de start e end nessa função é precisamente a mesma usada na função range.

Classificação de caracteres

Muitas vezes, é útil examinar um caractere e testar se ele é maiúsculo ou minúsculo, ou se é uma letra ou um dígito. O módulo string fornece várias constantes que são úteis para esses fins. Uma delas, string.digits é equivalente a “0123456789”. Ela pode ser usada para verificar se um caractere é um dígito usando o operador in.

O string string.ascii_lowercase contém todas as letras ASCII que o sistema considera serem minúsculas. Da mesma forma, string.ascii_uppercase contém todas as letras maiúsculas. string.punctuation contem todos os caracteres considerados símbolos de pontuação. Experimente o seguinte e veja o que acontece.

print(string.ascii_lowercase)
print(string.ascii_uppercase)
print(string.digits)
print(string.punctuation)

Para mais informações consulte a documentação do módulo string (veja Global Module Index).

Resumo

Esse capítulo introduziu várias novas ideias. O seguinte resumo pode ser-lhe útil para lembrar o que você aprendeu.

indexação ([])

Acessa um único caractere em um string usando sua posição (com início em 0). Exemplo: 'Isso'[2] resulta em 's'.

função len (comprimento)

Retorna o número de caracteres em um string. Exemplo: len('happy') resulta em 5.

percurso do laço for (for)

Percorrer um string significa acessar cada caractere no string, um de cada vez. Por exemplo, o seguinte laço for:

for ix in 'Exemplo':
    ...

executa o corpo do laço 7 vezes com diferentes valores de ix a cada vez.

fatiamento ([:])

Uma fatia (slice) é um substring de um string. Exemplo: 'sorvete de banana'[3:6] resulta em vet.

comparação de strings (>, <, >=, <=, ==, !=)

Os seis operadores relacionais (de comparação) comuns funcionam com strings e são avaliados de acordo com a ordem lexicográfica. Exemplos: 'apple' < 'banana' resulta em True. 'Zeta' < 'Appricot' resulta em False. 'Zebra' <= 'aardvark' resulta em True pois todas as letras maiúsculas precedem as minúsculas.

operadores in e not in (in, not in)

O operador in testa se um string está contido em outro. Exemplos: 'curar' in "Eu vou procurar voce." resulta em True. 'curdado' in "Eu vou procurar voce." resulta em False.

Glossário

branco (whitespace)

Qualquer caractere que move o cursor sem imprimir caracteres visíveis. A constante string.whitespace contém todos os caracteres “brancos”.

fatia

Uma parte de um string (substring) especificado por um intervalo de índices. De forma mais genérica, uma subsequência de qualquer tipo de sequência em Python pode ser criada usando o operador de fatia (sequencia[inicio:fim]).

imutável

Um tipo de dado composto cujos elementos não podem receber novos valores.

índice

Uma variável ou valor usado para selecionar um membro de uma coleção ordenada, como um caractere em um string, ou um elemento de uma lista.

notação ponto

Uso do operador ponto, ., para acessar funções dentro de um módulo, ou acessar métodos e atributos de um objeto.

parâmetro opcional

Um parâmetro definido no cabeçalho de uma função com a atribuição de um valor default que ele receberá caso nenhum argumento correspondente seja fornecido na chamada da função.

percurso

Para iterar pelos elementos de uma coleção, realizando uma operação similar para cada elemento.

tipo de dado coletivo

Um tipo de dado no qual os valores são constituidos por componentes, ou elementos, que são valores.

valor default

O valor dado para um parâmetro opcional caso nenhum argumento seja fornecido na chamada.

Exercícios

  1. Qual o resultado de cada um dos seguintes:

    1. ‘Python’[1]

    2. “Strings são sequências de caracteres.”[5]

    3. len(“maravilhoso”)

    4. ‘Mistério’[:4]

    5. ‘p’ in ‘Pineapple’

    6. ‘apple’ in ‘Pineapple’

    7. “pear” not in ‘Pineapple’

    8. ‘apple’> ‘pineapple’

    9. ‘pineapple’<’Peach’

  2. No livro Make Way for Ducklings de Robert McCloskey, os patinhos são chamados de Jack, Kack, Lack, Mack, Nack, Ouack, Pack, e Quack. Esse laço tenta imprimir esses nomes ordenadamente.

    prefixes = "JKLMNOPQ"
    suffix = "ack"
    
    for p in prefixes:
        print(p + suffix)
    

    é claro que esse programa tem problemas pois Ouack e Quack não são escritos corretamente. Você consegue consertar o programa?

  1. Atribua a uma variável em seu programa um string entre aspas triplas contendo seu parágrafo favorito de um poema, discurso, receita de bolo, etc.

    Escreva uma função que remove toda a pontuação de um string e conta o número de palavras no string que contém a letra ‘e’. Seu programa deve imprimir uma análise desse texto da seguinte forma:

    Seu texto contém 243 palavras, das quais 109 (44.8%) contém um 'e'.
    
  2. Imprima de forma organizada e formatada uma tabela de multiplicação, até 12 x 12.

  3. Escreva uma função que retorna o número de dígitos de um inteiro.

  4. Escreva uma função reverse que recebe um string e o retorna invertido.

  5. Escreva uma função que recebe um string e retorna o string espelhado.

  6. Escreva uma função que remove todas as ocorrências de uma letra de um string.

  7. Escreva uma função que reconhece palíndromes. (Dica: use a sua função reverse).

  8. Escreva uma função que conta o número de vezes que um substring ocorre em um string.

  9. Escreva uma função que remove a primeira ocorrência de um string de outro string.

  10. Escreva uma função que remove todas as ocorrências de um string de outro string.

  11. Esse é um outro sistema-L interessante chamado de curva de Hilbert. Use 90 graus.:

    L
    L -> +RF-LFL-FR+
    R -> -LF+RFR+FL-
    
  12. Essa é uma curva dragão. Use 90 graus.:

    FX
    X -> X+YF+
    Y -> -FX-Y
    
  13. Essa é uma curva chamada de cabeça de flecha (arrowhead). Use 60 graus.:

    YF
    X -> YF+XF+Y
    Y -> XF-YF-X
    
  14. Experimente a curva de Peano-Gosper. Use 60 graus.:

    FX
    X -> X+YF++YF-FX--FXFX-YF+
    Y -> -FX+YFYF++YF+FX--FX-Y
    
  15. Triângulo de Sierpinski. Use 60 graus.:

    FXF--FF--FF
    F -> FF
    X -> --FXF++FXF++FXF--
    
  16. Escreva uma função que implementa uma cifra de substituição. Em uma cifra de substituição uma letra é substituída por outra para codificar a mensagem. Por exemplo A -> Q, B -> T, C -> G etc. Sua função deve receber dois parâmetros, a mensagem que você deseja codificar e um string que representa o mapeamento das 26 letras no alfabeto. Sua função deve retornar um string que é a versão codificada da mensagem.

  17. Escreva uma função que decodifica a mensagem do exercício anterior. A função também deve receber dois parâmetros. A mensagem codificada, e o alfabeto para decodificação. A função deve retornar um string que corresponde à mensagem original antes de ser codificada.

  18. Escreva uma função desembaralha que recebe uma mensagem que foi embaralhada usando o algoritmo picket fence. Experimente trocar mensagens secretas com amigos e a mensagem abaixo (em inglês):

  19. Escreva uma função chamada rot13 que usa a cifra de César para codificar uma mensagem. A cifra de César funciona como uma cifra de substituição mas cada caractere é substituído pelo caractere a uma certa distância de sua posição. Por exemplo, se a distância for 4, então o alfabeto seria codificado da seguinte maneira: A->E, B->F, C->G, …, Z->D, etc. Observe que caracteres no final do alfabeto são transformados para caracteres no início, como uma lista circular. Dica: sempre que trabalhar com coisas circulares, é sempre bom considerar o operador módulo (%).

You have attempted of activities on this page