12. Funciones definidas por el Usuario

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.

Sintaxis de las Definiciones de Funciones

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.

Ejemplo de Definición de Función

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)

}

Llamada a Funciones definidas por el Usuario

            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.

La Sentencia return

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.

   
Índice
Manual