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
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-3: O que é impresso pelos seguintes comandos?
s = "python"
t = "rocks"
print(s+t)
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.
Q-4: O que é impresso pelos seguintes comandos?
s = "python"
excl = "!"
print(s+excl*3)
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.
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
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-6: O que é impresso pelos seguintes comandos?
s = "python rocks"
print(s[3])
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.
Q-7: O que é impresso pelos seguintes comandos?
s = "python rocks"
print(s[2] + s[-5])
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
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-11: O que é impresso pelos seguintes comandos?
s = "python rocks"
print(s.count("o") + s.count("p"))
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.
Q-12: O que é impresso pelos seguintes comandos?
s = "python rocks"
print(s[1]*s.index("n"))
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
11
-
O espaço em branco conta como um caractere.
12
-
Sim, há 12 caracteres no string.
Q-16: O que é impresso pelos seguintes comandos?
s = "python rocks"
print(len(s))
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.
Q-17: O que é impresso pelos seguintes comandos?
s = "python rocks"
print(s[len(s)-5])
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
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-20: O que é impresso pelos seguintes comandos?
s = "python rocks"
print(s[3:8])
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.
Q-21: O que é impresso pelos seguintes comandos?
s = "python rocks"
print(s[7:11]*3)
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
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-27: Qual o resultado da seguinte comparação:
"mel" < "melhor"
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-28: Qual o resultado da seguinte comparação:
"mel" < "Mel"
True
-
m é maior que M.
False
-
O comprimento não é importante.
Q-29: Qual o resultado da seguinte comparação:
"mel" < "Melhor"
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
Ball
-
Atribuição não é permitida com strings.
Call
-
Atribuição não é permitida com strings.
Error
-
Sim, strings são imutáveis.
Q-32: O que é impresso pelos seguintes comandos?
s = "Ball"
s[0] = "C"
print(s)
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
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-36: Quantas vezes a palavra OLA é impressa pelos seguintes comandos?
s = "viva o python"
for ch in s:
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.
Q-37: Quantas vezes a palavra OLA é impressa pelos seguintes comandos?
s = "viva o python"
for ch in s[3:8]:
print("OLA")
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.
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.
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
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.
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])
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.
Teste seu entendimento
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.
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
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.
Teste seu entendimento
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.
Q-50: O que é impresso pelos seguintes comandos:
s = "ball"
r = ""
for item in s:
r = item.upper() + r
print(r)
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:
As regras são muito parecidas com comandos if.
Vamos começar com um string e iterar sobre cada um de seus caracteres.
À 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 em5
.- 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 emvet
.- 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 emTrue
.'Zeta' < 'Appricot'
resulta emFalse
.'Zebra' <= 'aardvark'
resulta emTrue
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 emTrue
.'curdado' in "Eu vou procurar voce."
resulta emFalse
.
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¶
Qual o resultado de cada um dos seguintes:
‘Python’[1]
“Strings são sequências de caracteres.”[5]
len(“maravilhoso”)
‘Mistério’[:4]
‘p’ in ‘Pineapple’
‘apple’ in ‘Pineapple’
“pear” not in ‘Pineapple’
‘apple’> ‘pineapple’
‘pineapple’<’Peach’
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?
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'.
Imprima de forma organizada e formatada uma tabela de multiplicação, até 12 x 12.
Escreva uma função que retorna o número de dígitos de um inteiro.
Escreva uma função
reverse
que recebe um string e o retorna invertido.Escreva uma função que recebe um string e retorna o string espelhado.
Escreva uma função que remove todas as ocorrências de uma letra de um string.
Escreva uma função que reconhece palíndromes. (Dica: use a sua função
reverse
).Escreva uma função que conta o número de vezes que um substring ocorre em um string.
Escreva uma função que remove a primeira ocorrência de um string de outro string.
Escreva uma função que remove todas as ocorrências de um string de outro string.
Esse é um outro sistema-L interessante chamado de curva de Hilbert. Use 90 graus.:
L L -> +RF-LFL-FR+ R -> -LF+RFR+FL-
Essa é uma curva dragão. Use 90 graus.:
FX X -> X+YF+ Y -> -FX-Y
Essa é uma curva chamada de cabeça de flecha (arrowhead). Use 60 graus.:
YF X -> YF+XF+Y Y -> XF-YF-X
Experimente a curva de Peano-Gosper. Use 60 graus.:
FX X -> X+YF++YF-FX--FXFX-YF+ Y -> -FX+YFYF++YF+FX--FX-Y
Triângulo de Sierpinski. Use 60 graus.:
FXF--FF--FF F -> FF X -> --FXF++FXF++FXF--
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.
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.
Escreva uma função
desembaralha
que recebe uma mensagem que foi embaralhada usando o algoritmopicket fence
. Experimente trocar mensagens secretas com amigos e a mensagem abaixo (em inglês):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 (%
).