Posso determinar o tipo de uma variável awk?

3

Eu tenho a versão gawk do awk. Em esta parte do manual do gawk, é declarado que variáveis awk possuem "atributos" ", que são usados para determinar como tratá-los em várias operações.

Por exemplo, uma string que tenha a forma " +3.14" obtida pela análise da entrada tem o atributo STRNUM , que faz com que ela se comporte como um número em uma comparação com um número, enquanto a mesma sequência definida em um programa awk não possui este atributo.

OTOH, uma string como "3.14" aparentemente possui o atributo STRNUM , mesmo que tenha sido definido no programa porque o código x = "3.14" { print x == 3.14 } imprime 1. Considerando que, se o definirmos como "+3.13" ou " 3.14" , não tem o atributo STRNUM , pois o x = "+3.14" { print x == 3.14 } ou x = " 3.14" { print x == 3.14 } imprime 0.

Eu acho que tal sucinta na tipagem variável pode causar bugs sutis. Assim, a fim de ajudar na depuração de tais situações, existe uma maneira de aprender que tipo de "atributos" tem uma variável? Ou seja, podemos aprender qual é o tipo de variável?

    
por Utku 05.05.2016 / 15:47

4 respostas

4

O Awk tem 4 tipos : "number", "string", "string numérica" e "indefinido". Aqui é uma função para detectar isso:

function o_class(obj,   q, x, z) {
  q = CONVFMT
  CONVFMT = "% g"
    split(" " obj "" obj, x, "")
    x[1] = obj == x[1]
    x[2] = obj == x[2]
    x[3] = obj == 0
    x[4] = obj "" == +obj
  CONVFMT = q
  z["0001"] = z["1101"] = z["1111"] = "number"
  z["0100"] = z["0101"] = z["0111"] = "string"
  z["1100"] = z["1110"] = "strnum"
  z["0110"] = "undefined"
  return z[x[1] x[2] x[3] x[4]]
}

Para o terceiro argumento de split , você precisa de algo que não seja um espaço e não faz parte de obj ou será tratado como um delimitador. Eu escolhi com base na sugestão de Stéphane . A função faz internamente CONVFMT alternando, por isso deve retornar o resultado correto, independentemente do valor CONVFMT no momento da chamada de função:

split("12345.6", q); print 1, o_class(q[1])
CONVFMT = "%.5g"; split("12345.6", q); print 2, o_class(q[1])
split("nan", q); print 3, o_class(q[1])
CONVFMT = "%.6G"; split("nan", q); print 4, o_class(q[1])

Resultado:

1 strnum
2 strnum
3 strnum
4 strnum

Suíte de testes completa:

print 1, o_class(0)
print 2, o_class(1)
print 3, o_class(123456.7)
print 4, o_class(1234567.8)
print 5, o_class(+"inf")
print 6, o_class(+"nan")
print 7, o_class("")
print 8, o_class("0")
print 9, o_class("1")
print 10, o_class("inf")
print 11, o_class("nan")
split("00", q); print 12, o_class(q[1])
split("01", q); print 13, o_class(q[1])
split("nan", q); print 14, o_class(q[1])
split("12345.6", q); print 15, o_class(q[1])
print 16, o_class()

Resultado:

1 number
2 number
3 number
4 number
5 number
6 number
7 string
8 string
9 string
10 string
11 string
12 strnum
13 strnum
14 strnum
15 strnum
16 undefined

A notável fraqueza é: se você fornecer "string numérica" de qualquer um dos a seguir, a função retornará incorretamente "number":

  • inteiro
  • inf
  • -inf

Para números inteiros, isso é explicado:

A numeric value that is exactly equal to the value of an integer shall be converted to a string by the equivalent of a call to the sprintf function with the string %d as the fmt argument

No entanto, inf e -inf também se comportam dessa maneira; isto é, que nenhum dos o acima pode ser influenciado pela variável CONVFMT :

CONVFMT = "% g"
print "" .1
print "" (+"nan")
print "" 1
print "" (+"inf")
print "" (+"-inf")

Resultado:

 0.1
 nan
1
inf
-inf

Na prática, isso não importa, veja o teste de

.

    
por 07.05.2017 / 06:18
3

Com o gawk, PROCINFO["identifiers"] é um array com informações sobre variáveis . Use como: PROCINFO["identifiers"]["your_variable_name"] . O valor possível retornado é um dos "array", "builtin", "extension", "scalar", "untyped", "user".

Existe apenas um scalar geral, que inclui cadeias e números. O intérprete gawk tenta o seu melhor para fazer coisas.

Existe uma razão pela qual às vezes você verá um variable + 0 aparentemente redundante em algum lugar, para garantir que awk trate a variável como numérica.

Veja este parágrafo para alguns dos truques conversões implícitas .

    
por 05.05.2016 / 17:09
2

Para esclarecer, apenas strings que estão vindo de algumas fontes (aqui citando a especificação POSIX ):

  1. Field variables
  2. Input from the getline() function
  3. FILENAME
  4. ARGV array elements
  5. ENVIRON array elements
  6. Array elements created by the split() function
  7. A command line variable assignment
  8. Variable assignment from another numeric string variable

devem ser considerados uma string numérica se seu valor for numérico (permitindo espaços em branco iniciais e finais, com variações entre implementações em suporte a hex, octal, inf, nan ...).

A constante de string "3.14" literal é uma string, não strnum, porque não vem de uma dessas fontes.

x = "3.14"; if (x == 3.14) print "yes"

imprime sim, mas isso é porque está fazendo uma comparação lexical (dependendo da implementação, usando memcmp() , strcmp() ou strcollate() ) de 3.14 e a conversão para uma string (por meio do formato CONVFMT string, %.6g in gawk e muitas outras implementações) desse 3.14 number. Ou seja, com esse valor de CONVFMT , (x == 3.14) é igual a (x == "3.14") .

(x < 12) seria falso, porque 3.14 classifica lexicamente após 12 (o mesmo que ("3.14" < "12") ). Com CONVFMT = "%.6e" , (x == 3.14) também retornaria false porque isso se torna ("3.14" == "3.140000e+00") .

Por outro lado, em:

"echo \"3.1400 \"" | getline x
if (x == 3.14) print "yes"
if (x < 12) print "yes"

yes é impresso duas vezes, seja qual for o valor de CONVFMT , porque uma comparação numérica é executada. x é um strnum porque vem de getline e tem um valor numérico.

Ele ainda mantém seu valor de string. print x imprimirá "3.1400 " , qualquer que seja o valor de OFMT ou CONVFMT .

E:

"echo 3.14 foo" | getline x
if (x == 3.14) print "yes"

Não imprime yes . x vem de getline , mas não possui um valor numérico (por causa do foo ). É uma string normal, como se você tivesse escrito x = "3.14 foo" . Ainda assim, você poderá fazer operações numéricas com ele:

print x + 1

Será emitido 4.14 . Acima, porque ela está envolvida em uma operação numérica, a string é convertida em um número, observando a parte inicial (além dos espaços em branco eventuais) que se parece com um número no início de uma string.

Portanto, (x+0 == 3.14) e (x+0 < 12) também retornarão verdadeiro. x+0 é numérico, então temos uma comparação numérica.

Observe que inf , nan , Infinity não são reconhecidos como os valores especiais de ponto flutuante inf ou nan como constantes, mas em várias implementações awk , você pode usar ("inf"+0) .

    
por 10.05.2017 / 11:18
2

No GNU Awk 4.2, há uma nova função typeof() para verificar isso, conforme indicado nas notas da versão beta:

  1. The new typeof() function can be used to indicate if a variable or array element is an array, regexp, string or number. The isarray() function is deprecated in favor of typeof().

Agora você pode dizer:

$ awk 'BEGIN {print typeof("a")}'
string
$ awk 'BEGIN {print typeof(1)}'
number
$ awk 'BEGIN {print typeof(a[1])}'
unassigned
$ awk 'BEGIN {a[1]=1; print typeof(a)}'
array
$ echo ' 1 ' | awk '{print typeof($0)}'
strnum
    
por 10.10.2017 / 10:49