Una colección es un conjunto de objetos. Los arrays y las listas son
colecciones. Ya sabemos que una String es una colección;
concretamente, una colección de caracteres. Muchos tipos de colecciones
tienen comportamientos similares.
Un Array es una colección de tamaño fijo y, a diferencia de
una cadena, puede contener cualquier tipo de literal entre
#( ):
"array of numbers" #(1 3 5 7 11 1.1) "array of mixed literals" #(1 'friend' $& 'al')
Un Array se construye directamente utilizando elementos literales bien formados. Llegaremos al significado de esta última afirmación cuando discutamos los detalles del lenguaje Smalltalk.
Por ahora, sólo observa cómo usando expresiones no literales al construir un array no funcionará como esperas:
#(1 2/3) ⇒ #(1 2 #/ 3)
De hecho, el símbolo $/ se interpreta como un símbolo
literal y obtenemos los componentes básicos de «2 / 3», pero este texto
no se interpreta como una fracción. Para insertar una fracción en la
matriz, se utiliza un array en tiempo de ejecución o un array
dinámico, cuyos elementos son expresiones separadas por puntos y
rodeadas por { }:
{1 . 2/3 . 7.5}
⇒ #(1 2/3 7.5)
Con un array relleno de números puedes obtener información y operaciones aritméticas:
#(1 2 3 4) size ⇒ 4 #(1 2 3 4) + 10 ⇒ #(11 12 13 14) #(1 2 3 4) / 10 ⇒ #(1/10 1/5 3/10 2/5)
Las operaciones matemáticas también funcionan:
#(1 2 3 4) squared ⇒ #(1 4 9 16) #(0 30 45 60) degreeCos ⇒ #(1.0 0.8660254037844386 0.7071067811865475 0.49999999999999994)
También se pueden utilizar directamente métodos estadísticos básicos en arrays de números:
#(7.5 3.5 8.9) mean ⇒ 6.633333333333333 #(7.5 3.5 8.9) range ⇒ 5.4 #(7.5 3.5 8.9) min ⇒ 3.5 #(7.5 3.5 8.9) max ⇒ 8.9
Para obtener un array con los números naturales desde el 1 al 100,
utilizaremos el mensaje #to:
(1 to: 100) asArray ⇒ #(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100)
En esta línea de código, el mensaje #to: se envía a 1
con el argumento 100. Devuelve un objeto intervalo. El
mensaje #asArray enviado al intervalo devuelve un array.
Crea un array con los números enteros con un rango de -80 a 50.
Ejercicio 4.2: Números negativos enteros
El tamaño de un array es fijo, no puede crecer. Una
OrderedCollection es una colección dinámica y ordenada. Crece
cuando se añade un elemento con el mensaje #add:
| fibo | fibo := OrderedCollection newFrom: #(1 1 2 3). fibo add: 5; add: 8; add: 13; add: 21. fibo ⇒ an OrderedCollection(1 1 2 3 5 8 13 21)
Ejemplo 4.1: Colección de tamaño dinámico
El acceso al índice de los elementos de una colección se realiza mediante diversos mensajes. El índice abarca naturalmente desde 1 hasta el tamaño de la colección:
fibo at: 1 ⇒ 1 fibo at: 6 ⇒ 5 fibo last ⇒ 21 fibo indexOf: 2 ⇒ 3 fibo at: fibo size ⇒ 21
Una colección incluye un conjunto de métodos útiles denominados enumeradores. Los enumeradores operan sobre cada elemento de una colección.
Las operaciones de conjunto entre dos colecciones se calculan con los mensajes
#union:, #intersection: y #difference:.
#(1 2 3 4 5) intersection: #(3 4 5 6 7) ⇒ #(3 4 5) #(1 2 3 4 5) union: #(3 4 5 6 7) ⇒ a Set(5 4 3 2 7 1 6) #(1 2 3 4 5) difference: #(3 4 5 6 7) ⇒ #(1 2)
Ejemplo 4.2: Operaciones de conjunto
Construye la matriz de los números 1,...,24,76,...,100.
Ejercicio 4.3: Agujero en un conjunto
Las operaciones de conjunto funcionan con cualquier tipo de objeto. La comparación de objetos merece una sección aparte.
#(1 2 3 'e' 5) intersection: #(3.0 4 6 7 'e') ⇒ #(3 'e')
Para seleccionar los números primos del 1 al 100, utilizamos el
enumerador #select:. Este mensaje se envía a una colección y, a
continuación, selecciona cada elemento de la colección que devuelve
verdadero a una condición de prueba:
(1 to: 100) select: [ :n | n isPrime ] ⇒ #(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97)
Ejemplo 4.3: Seleccionar los números primos entre el 1 y 100
Este ejemplo introduce el mensaje #select: y un bloque de código,
un elemento constitutivo primordial del modelo Cuis-Smalltalk. Un bloque de
código, delimitado por corchetes, es una parte del código para su
posterior ejecución. Expliquemos cómo se evalúa este script:
(1 to: 100) se evalúa como un intervalo
[:n | n isPrime] está instanciado (creado)
#select: is sent to the interval with the
block of code as the argument
select:, por cada entero del intervalo, se
invoca el bloque de código con su parámetro n fijado al
valor del entero. Un A parámetro de bloque comienza con «:» y es
un identificar normal 18. A continuación, cada vez que n isPrime se evalúa
como verdadero, el valor n se añade a una nueva colección respondida
cuando el método select: termina de comprobar cada elemento de la
colección.
Un bloque de código se puede guardar en una variable, pasar como parámetro y utilizar varias veces.
| add2 |
add2 := [:n| n + 2].
{ add2 value: 2. add2 value: 7 }.
⇒ #(4 9)
Los enumeradores implementan formas tremendamente potentes de procesar colecciones sin necesidad de un índice. Con esto queremos decir que son fáciles de usar correctamente. ¡Nos gusta lo sencillo!
Para hacerse una idea de lo útiles que son los enumeradores, eche un
vistazo a la clase Collection en la categoría de métodos
enumerating.
Selecciona los números impares entre -20 y 45.
Ejercicio 4.4: Enteros impares
Quieres saber cuántos números primos son menores de 100. Envía el
mensaje #size a la colección respondida en el Ejemplo 4.3. Los
paréntesis son obligatorios para asegurarnos de que #size se envía
la última colección resultante:
( (1 to: 100) select: [:n | n isPrime] ) size ⇒ 25
Ejemplo 4.4: Cuenta cuántos números primos hay entre 1 y 100
Para mayor claridad, utilizamos una variable llamada
primeNumbers para almacenar la lista de números primos que
hemos creado:
| primeNumbers | primeNumbers := (1 to: 100) select: [:n | n isPrime]. primeNumbers size
Modifica el Ejemplo 4.4 para calcular la cantidad de números primos entre 101 y 200.
Ejercicio 4.5: Números primos entre 101 y 200
Haz una lista de los múltiplos de 7 menores de 100..
Ejercicio 4.6: Múltiplos de 7
Construye una colección de los interos impares en [1 ; 100] que no sean primos.
Ejercicio 4.7: Impares y no primos
Una enumeración hermana de @.msg select:@ es @.msg collect:@. Devuelve una nueva colección del mismo tamaño, con cada elemento transformado por un bloque de código. Al buscar raíces cúbicas perfectas, es útil conocer algunos cubos:
(1 to: 10) collect: [:n | n cubed] ⇒ #(1 8 27 64 125 216 343 512 729 1000)
Ejemplo 4.5: Recoger cubos
Los elementos recopilados pueden ser de diferentes tipos. A continuación, se enumera una cadena y se recopilan números enteros:
'Bonjour' collect: [:c | c asciiValue ] ⇒ #(66 111 110 106 111 117 114)
Podemos cambiar el valor ASCII, convertirlo de nuevo en un carácter y luego recopilarlo en una nueva cadena. Es un cifrado sencillo:
'Bonjour' collect: [:c | Character codePoint: c asciiValue + 1 ] ⇒ 'Cpokpvs'
Ejemplo 4.6: Cifrado sencillo
Escribe un script para decodificar el cifrado ’Zpv!bsf!b!cptt’, está codificado como el Ejemplo 4.6.
Ejercicio 4.8: Decodificar cifrado
El cifrado de César se basa en desplazar las letras hacia la derecha en el orden alfabético. El método lleva el nombre de Julio César, quien lo utilizó en su correspondencia privada con un desplazamiento de 3.
Escribe un script para generar el alfabeto en letras mayúsculas que representa el cifrado Caesar. La respuesta esperada es #($D $E $F $G $H $I $J $K $L $M $N $O $P $Q $R $S $T $U $V $W $X $Y $Z $A $B $C).
Ejercicio 4.9: Alfabeto del cifrado Caesar
Una vez que hayas descifrado correctamente el alfabeto, podrás codificar tu primer mensaje:
Codifica la frase ’SMALLTALKEXPRESSION’.
Ejercicio 4.10: Codifica con el cifrado Caesar
Y decodifica el mensaje:
Decodifica esta famosa cita atribuida a Julio Caesar ’DOHDMDFWDHVW’.
Ejercicio 4.11: Decodifica con el cifrado de Caesar
La recopilación se puede iterar con bucles tradicionales: existe toda una familia de bucles repeat, while y for.
Un bucle for simple entre dos valores enteros se escribe con la
palabra clave #to:do:, el último argumento es un bloque de
código que se ejecuta para cada índice:
| sequence | sequence := OrderedCollection new. 1 to: 10 do: [:k | sequence add: 1 / k]. sequence ⇒ an OrderedCollection(1 1/2 1/3 1/4 1/5 1/6 1/7 1/8 1/9 1/10)
Ejemplo 4.7: Un bloque for
Sin embargo, un recolecta escribe más concisamente:
(1 to: 10) collect: [:k | 1 / k]
Un paso con un valor diferente a 1, se inserta un tercer argumento numérico:
1 to: 10 by: 0.5 do: [:k | sequence add: 1 / k]
Un bucle repetido sin índice ni colección alguna se escribe con el
mensaje #timesRepeat: enviado a un entero:
| fibo | fibo := OrderedCollection newFrom: #(1 1). 10 timesRepeat: [ fibo add: (fibo last + fibo atLast: 2)]. fibo ⇒ an OrderedCollection(1 1 2 3 5 8 13 21 34 55 89 144)
Ejemplo 4.8: Un bucle repeat
El cociente de los términos consecutivos de Fibonacci converge hacia el valor áureo.:
fibo pairsDo: [:i :j | Transcript show: (j / i ) asFloat ; cr] ⇒ 1.0 ⇒ 1.5 ⇒ 1.6 ⇒ 1.6153846153846154 ⇒ 1.6176470588235294 ⇒ 1.6179775280898876
Un identificador sólo es una palabra que comienza por una letra minúscula y está formada por letras minúsculas y mayúsculas y dígitos. Todos los nombres de variable son identificadores.