Programas awk complicados pueden ser simplicados a menudo definiendo tus propias funciones.
Las funciones definidas por el usuario pueden ser llamadas del mismo modo que
las funciones implícitas. (Ver la sección Llamada
a funciones implícitas (Built-in),
pero tú eres el encargado de definirlas y decirle a awk
qué tiene que hacer.
La definición de funciones puede aparecer en cualquier
parte entre las reglas de un programa awk. Po lo que la forma general de un programa awk
se extiende para incluir secuencias de reglas y definiciones de funciones definidas
por el usuario.
La definición de una función sería algo como esto:
function nombre_función (lista_de_parámetros) {
cuerpo-de-la-función
}
La palabra clave function puede ser abreviada a func.
- Nombre_función es el nombre de la función que se va a definir. Un nombre de función válido tiene la misma forma que un nombre de variable válido: una secuencia de letras, dígitos y subrayados, siempre que no comience con un dígito.
- Lista_de_parámetros es una lista de los argumentos de las funciones y nombres de variables locales, separadas por comas. Cuando se llama la función, los nombres de los argumentos se usan para guardar los valores de los argumentos pasados en la llamada a la función. Las variables locales son inicializadas a la cadena nula.
- El cuerpo-de-la-función consiste en sentencias de awk. Es la parte más importante de la definición, porque dice lo que la función debería hacer realmente. Los nombres de argumentos existen para darle al cuerpo una forma de referirse a los argumentos; variables locales, para darle al cuerpo sitios donde guardar valores temporales.
Los nombres de argumentos no se distinguen sintácticamente de los nombres de
variables locales; en su lugar, el número de argumentos suministrados cuando
se llama a la función determina cuantas variables argumento hay. Por lo que,
si se dan tres valores de argumento, los tres primeros nombres en lista-de-parámetros
son argumentos, y el resto son variables locales.
Por lo que si el número de argumentos no es el mismo
en todas las llamadas que tenga la función, algunos de los nombres en lista-de-parámetros
podrían ser argumentos en algunas ocasiones y variables locales en otras ocasiones.
Otra forma de enfocar esto es que los argumentos omitidos tienen el valor por
defecto de cadenas nulas.
Normalmente cuando escribes una función sabes cuantos
nombres tratas de usar como argumentos y cuantos tratas de usar como variables
locales. Por convención, deberías escribir un espacio extra entre los argumentos
y las variables locales, de forma que otras personas puedan serguir como se
supone que se usa la función.
Durante la ejecución del cuerpo de la función, los
valores de los argumentos y variables locales esconden cualquier variable del
mismo nombre usada en el resto del programa. Las variables que tienen el mismo
nombre que las variables locales de la función no son accesibles en la definición
de la función, porque no existe ninguna forma de nombrarlas debido a que sus
nombres son usados para variables locales. El resto de las variables usadas
en el programa awk pueden ser referenciadas o fijadas de forma
normal en la definición de la función.
Los argumentos y variables locales permanecen solo
mientras se esté ejecutando el cuerpo de la función. Una vez que el cuerpo de
la función acaba, las variables del programa principal que se llaman igual que
las variables locales de la función se pueden usar de nuevo.
El cuerpo de la función puede contener expresiones
con llamadas a funciones. Pueden incluso llamarse a si misma directamente o
a través de un función intermedia, a estas funciones se les dice que son recursivas.
No existe la necesidad en awk
de poner la definición de la función antes de usarla. Esto se debe a que awk
lee el programa completo antes de comenzar a ejecutarlo.
Aquí se muestra un ejemplo de función definida por
el usuario, llamada miprint, la cual toma como parámetro un número y
lo imprime en un formato especifico.
function miprint (num)
{
printf "%6.3g\n", num
}
Para ilustrar una llamada a la función miprint, aquí se presenta una regla awk que usar nuestra función miprint:
$3 > 0 { miprint($3) }
Este programa imprime, en nuestro formato especial,
todos los terceros campos que contengan un número positivo en nuetra entrada.
Por lo tanto, cuando se le da:
1.2 3.4 5.6 7.8
9.10 11.12 13.14 15.16
17.18 19.20 21.22 23.24
este programa, usando nuestra función para formatear el resultado, imprime:
5.6
13.1
21.2
Aquí está un ejemplo mejor de una función recursiva.
Imprime una cadena al revés:
function rev (str, len) {
if (len == 0) {
printf "\n"
return
}
printf "%c", substr(str, len, 1)
rev(str, len - 1)
}
La llamada a una función hace que se ejecute la función y realice su trabajo. Una llamada a función es una expresión, y su valor es el valor devuelto por la función.
Una llamada a función consiste en el nombre de la función seguido por los argumentos entre paréntesis. Lo que escribes en la llamada para los argumentos son expresiones awk; cada vez que se ejecute la llamada, estas expresiones son evaluadas, y sus valores son los argumentos actuales. Por ejemplo, aquí aparece una llamada a foo con tres argumentos:
foo(x y, "lose", 4 * z)
Nota: los espacios en blanco (espacios y tabuladores)
no están permitidos entre el nombre de la función y el paréntesis de apertura
de la lista de argumentos. Si escribes un espacio en blanco por error, awk podría pensar que deseas concatenar una variable
con la expresión entre paréntesis. Sin embargo, te avisa de que usaste un nombre
de función en lugar de un nombre de variable, y devuelve un error.
Cuando una función es llamada, se le
proporciona una copia de los valores de sus argumentos. A esto se le llama llamada
por valor. El llamador podría usar una variable como la expresión para el
argumento, pero la función llamada no sabe esto: todo lo que sabe es el valor
que tiene el argumento. Por ejemplo, si escribes este código:
foo = "bar"
z = myfunc(foo)
entonces no deberías pensar en el argumento de myfunc como si fuese la variable foo. En su lugar, piensa en el argumento como la cadena, bar.
Si la función myfunc
altera los valores de sus variables locales, esto no tiene efecto en cualquier
otra variable. En particular, si myfunc
hace esto:
function myfunc (win) {
print win
win = "zzz"
print win
}
para cambiar su primer variable argumento win, esto no cambia el valor de foo de la llamada. La función de foo en la llamada a myfunc acabó cuando su valor, bar, se procesó. Si win existe también fuera de myfunc, el cuerpo de la función no puede alterar este valor externo, porque está escondido durante la ejecución de myfunc y no puede ser visto o alterado desde allí.
Sin embargo, cuando los parámetros de las funciones
son arrays, ellos no pueden ser copiados. En su lugar, el mismo
array está disponible para la manipulación directa por parte de la función.
A esto se le llama normalmente llamada por referencia.
Los cambios realizados a un parámetro array dentro del cuerpo de la función
son visibles fuera de dicha función. Esto podría ser peligroso si
no te fijas en lo que estás haciendo. Por ejemplo:
function changeit (array, ind, nvalue) {
array[ind] = nvalue
}
BEGIN {
a[1] = 1 ; a[2] = 2 ; a[3] = 3
changeit(a, 2, "two")
printf "a[1] = %s, a[2] = %s, a[3] = %s\n", a[1], a[2], a[3]
}
imprime `a[1] = 1, a[2] = two, a[3] = 3', porque la llamada a changeit almacena two en el segundo elemento de a.
El cuerpo de las funciones definidas por el usuario
puede contener una sentencia return. Esta sentencia devuelve el control
al resto del programa awk. Puede ser también usada para devolver un valor para que sea usado en
el resto del programa. Esta sentencia presenta la siguiente forma:
return expresión
La parte expresión es opcional. Si se omite,
entonces el valor devuelto es indefinido y, por lo tanto, impredecible. Una
sentencia return sin valor de expresión se asume por defecto al final
de todas las definiciones de función. Así que si el control llega al final de
la definición de función, entonces la función devuelve un valor impredecible.
Aquí está un ejemplo de una función definida por usuario
que devuelve el valor del número mayor contenido entre los elementos de un
array:
function maxelt (vec, i, ret) {
for (i in vec) {
if (ret == "" ¦¦ vec[i] > ret)
ret = vec[i]
}
return ret
}
Llamas a maxelt con un argumento, un nombre de array. Las
variables locales i y ret
no intentan ser argumentos; mientras no exista nada que te detenga de pasar
dos o más argumentos a maxelt, los resultados serían extraños. El espacio
extra antes de i en la lista de parámetros de la función es para
indicar que i y ret
no se suponen que sean argumentos. Esta es una convención que deberías seguir
cuando definas funciones.
Aquí está un programa que usa nuestra función maxelt. Carga un array, llama maxelt,
y entonces reporta el número máximo de ese array:
awk '
function maxelt (vec, i, ret) {
for (i in vec) {
if (ret == "" ¦¦ vec[i] > ret)
ret = vec[i]
}
return ret
}
# Load all fields of each record into nums.
{
for(i = 1; i <= NF; i++)
nums[NR, i] = $i
}
END {
print maxelt(nums)
}'
Dada la siguiente entrada:
1 5 23 8 16
44 3 5 2 8 26
256 291 1396 2962 100
-6 467 998 1101
99385 11 0 225
nuestro programa nos informa (predeciblemente) que:
99385
es el número mayor en nuestro array.
Manual |