How does the following quine work? - perl

According to wikipedia :
A quine is a non-empty computer program which takes no input and produces a copy of its own source code as its only output
I saw this piece of perl code and am not able to figure out how it works.
Save the following line in file /tmp/p and run the file as perl /tmp/p:
Illegal division by zero at /tmp/p line 1.
The output of perl /tmp/p is:
Illegal division by zero at /tmp/p line 1.
How is the code working?

First, try to run it with warnings turned on:
$ perl -w p
Unquoted string "at" may clash with future reserved word at p line 1.
Unquoted string "tmp" may clash with future reserved word at p line 1.
Argument "tmp" isn't numeric in division (/) at p line 1.
Argument "at" isn't numeric in division (/) at p line 1.
The first two warnings are from the compile phase.
Let's look at the Deparse output:
$ perl -MO=Deparse p
'division'->Illegal('zero'->by('at' / 'tmp' / 'line'->p(1)));
p syntax OK
In essence, the value of at divided by tmp divided by the return value of another method invocationp is passed as an argument to the method by invoked on the class 'zero'. at and tmp are considered to be strings, and their numeric values are zero. Therefore, at/tmp results in the illegal division by zero error.
You will get the same error if you change the file's contents to
Stackoverflow hacker news one at /tmp/p line 1.
If you are wondering how Illegal division becomes 'division'->Illegal, see indirect object syntax, and avoid using it.

I would prefer to see you concentrating on improving the quality of your Perl code, rather than investigating obscure corners
But the answer is that the line is parsed as
'division'->Illegal('zero'->by('at' / 'tmp' / 'line'->p(1)));
and Perl uses zero for 'at' and 'tmp' because they are not valid numeric strings, so the first action is to evaluate 0 / 0 which throws the error

Related

When I run a file which is begin with "#!/usr/bin/perl -w", I get a error: "syntax error at line 153, near "=~ ?""

When I run a file which is begin with #!/usr/bin/perl -w, I get a error:
syntax error at line 153, near "=~ ?"
I try to add "#!/bin/bash", this error is not append, but I get another
error:
"line 34: syntax error near unexpected token `('"
line 153 in my file:
($output_volume =~ ?^([\S]+).mnc?) && ($base_name = $1) ||
die "sharpen_volume failed: output volume does not appear to be"
." a minc volume.\n";
line34 in my file:
use MNI::Startup qw(nocputimes);
$output_volume =~ ?^([\S]+).mnc?
This used to be valid perl and thus might appear in old code and instructional material.
From perlop:
In the past, the leading m in m?PATTERN? was optional, but omitting it would produce a deprecation warning. As of v5.22.0, omitting it produces a syntax error. If you encounter this construct in older code, you can just add m.
That is Perl code so the first error message is meaningful.
With delimiters other than // in the match operator you must have the explicit m for it, so
$output_volume =~ m?^([\S]+).mnc?
It is only with // delimiters that the m may be omitted; from Regex Quote-Like Operators (perlop)
If "/" is the delimiter then the initial m is optional.
See perlretut for a tutorial introduction to regex and perlre for reference.
Also note that the particular delimiters of ? trigger specific regex behavior in a special case. This is discussed by the end of the documentation section in perlop linked above.
You already have two answers that explain the problem.
? ... ? is no longer valid syntax for a match operator. You need m? ... ? instead.
Until Perl 5.22, your syntax generated a warning. Now it's a fatal error (which is what you are seeing). So I assume you're now running this on a more recent version of Perl.
There are, however, a few other points it is probably worth making.
You say you tried to investigate this by changing the first line of your file from #!/usr/bin/perl -w to #!/bin/bash. I'm not sure how you think this was going to help. This line defines the program that is used to run your code. As you have Perl code, you need to run it with Perl. Trying to run it with bash is very unlikely to be useful.
The m? ... ? (or, formerly, ? ... ?) syntax triggers an obscure and specialised behaviour. It seems to me that this behaviour isn't required in your case, so you can probably change it to the more usual / ... /.
Your regex contains an unescaped dot character. Given that you seem to be extracting the basename from a filename that has an extension, it seems likely that this should be escaped (using \.) so that it matches an actual dot (rather than any character).
If you are using this code to extract a file's basename, then using a regex probably isn't the best approach. Perhaps take a look at File::Basename instead.

"Uninitialized value" error when running word_align.pl script

I'm trying to run the word_align.pl script provided by CMUSphinx. I write the command as follows:
perl word_align.pl actualtext.txt batchOutputText.txt
But the terminal gives me the following errors:
Use of uninitialized value $ref_uttid in hash element at word_align.pl line 60, line 1.
Use of uninitialized value $ref_uttid in concatenation (.) or string at word_align.pl line 61, line 1.
UttID is not ignored but it could not found in any entries of the hypothesis file on line3 1 UTTID
I am not quite familiar with Perl and I can't figure out what is the problem here though I followed the instructions provided by CMUSphinx to run that script
You can find the script here
Edit: here is the reference's file link
The answer is in this error message
UttID is not ignored but it could not found in any entries of the hypothesis file on line3 1 UTTID
The reference file that you are passing is malformed, specifically its first line isn't formatted as it should be
More precisely, each line of the reference file requires a UTT ID—a unique string in parentheses like (output00000). It must be unique because it is used as a hash key. A simple digit like (1) won't work as it will be mistaken for an alternative pronunciation
The first line of your file must be different from that. You suggest
<s> text </s> (file12)
which actually works fine—I have tested it—and $ref_uttid comes out as FILE12. If you tell us what is actually in your file then I am sure we could help you better

How to use command line arguments in Fortran?

GCC version 4.6
The Problem: To find a way to feed in parameters to the executable, say a.out, from the command line - more specifically feed in an array of double precision numbers.
Attempt: Using the READ(*,*) command, which is older in the standard:
Program test.f -
PROGRAM MAIN
REAL(8) :: A,B
READ(*,*) A,B
PRINT*, A+B, COMMAND_ARGUMENT_COUNT()
END PROGRAM MAIN
The execution -
$ gfortran test.f
$ ./a.out 3.D0 1.D0
This did not work. On a bit of soul-searching, found that
$./a.out
3.d0,1.d0
4.0000000000000000 0
does work, but the second line is an input prompt, and the objective of getting this done in one-line is not achieved. Also the COMMAND_ARGUMENT_COUNT() shows that the numbers fed into the input prompt don't really count as 'command line arguments', unlike PERL.
If you want to get the arguments fed to your program on the command line, use the (since Fortran 2003) standard intrinsic subroutine GET_COMMAND_ARGUMENT. Something like this might work
PROGRAM MAIN
REAL(8) :: A,B
integer :: num_args, ix
character(len=12), dimension(:), allocatable :: args
num_args = command_argument_count()
allocate(args(num_args)) ! I've omitted checking the return status of the allocation
do ix = 1, num_args
call get_command_argument(ix,args(ix))
! now parse the argument as you wish
end do
PRINT*, A+B, COMMAND_ARGUMENT_COUNT()
END PROGRAM MAIN
Note:
The second argument to the subroutine get_command_argument is a character variable which you'll have to parse to turn into a real (or whatever). Note also that I've allowed only 12 characters in each element of the args array, you may want to fiddle around with that.
As you've already figured out read isn't used for reading command line arguments in Fortran programs.
Since you want to read an array of real numbers, you might be better off using the approach you've already figured out, that is reading them from the terminal after the program has started, it's up to you.
The easiest way is to use a library. There is FLAP or f90getopt available. Both are open source and licensed under free licenses.
The latter is written by Mark Gates and me, just one module and can be learned in minutes but contains all what is needed to parse GNU- and POSIX-like command-line options. The first is more sophisticated and can be used even in closed-source projects. Check them out.
Furthermore libraries at https://fortranwiki.org/fortran/show/Command-line+arguments
What READ (*,*) does is that it reads from the standard input. For example, the characters entered using the keyboard.
As the question shows COMMAND_ARGUMENT_COUNT() can be used to get the number of the command line arguments.
The accepted answer by High Performance Mark show how to retrieve the individual command line arguments separated by blanks as individual character strings using GET_COMMAND_ARGUMENT(). One can also get the whole command line using GET_COMMAND(). One then has to somehow parse that character-based information into the data in your program.
I very simple cases you just need the program requires, for example, two numbers, so you read one number from arg 1 and another form arg 2. That is simple. Or you can read a triplet of numbers from a single argument if they are comma-separated like 1,2,3 using a simple read(arg,*) nums(1:3).
For general complicated command line parsing one uses libraries such as those mentioned in the answer by Hani. You have set them up so that the library knows the expected syntax of the command line arguments and the data it should fill with the values.
There is a middle ground, that is still relatively simple, but one already have multiple arguments, that correspond to Fortran variables in the program, that may or may not be present. In that case one can use the namelist for the syntax and for the parsing.
Here is an example, the man point is the namelist /cmd/ name, point, flag:
implicit none
real :: point(3)
logical :: flag
character(256) :: name
character(1024) :: command_line
call read_command_line
call parse_command_line
print *, point
print *, "'",trim(name),"'"
print *, flag
contains
subroutine read_command_line
integer :: exenamelength
integer :: io, io2
command_line = ""
call get_command(command = command_line,status = io)
if (io==0) then
call get_command_argument(0,length = exenamelength,status = io2)
if (io2==0) then
command_line = "&cmd "//adjustl(trim(command_line(exenamelength+1:)))//" /"
else
command_line = "&cmd "//adjustl(trim(command_line))//" /"
end if
else
write(*,*) io,"Error getting command line."
end if
end subroutine
subroutine parse_command_line
character(256) :: msg
namelist /cmd/ name, point, flag
integer :: io
if (len_trim(command_line)>0) then
msg = ''
read(command_line,nml = cmd,iostat = io,iomsg = msg)
if (io/=0) then
error stop "Error parsing the command line or cmd.conf " // msg
end if
end if
end subroutine
end
Usage in bash:
> ./command flag=T name=\"data.txt\" point=1.0,2.0,3.0
1.00000000 2.00000000 3.00000000
'data.txt'
T
or
> ./command flag=T name='"data.txt"' point=1.0,2.0,3.0
1.00000000 2.00000000 3.00000000
'data.txt'
T
Escaping the quotes for the string is unfortunately necessary, because bash eats the first quotes.

Error: Can't take log of -9.4351e+0.007

I am creating a mini search engine using Perl.While doing so I am using a formula with log to the base 10. However for some value I am getting an error:
Can't take log of -9.4351e+0.007.
It is impossible to track where I am getting this error from. I just want to ignore this case. How can this be handled in Perl. Subroutine for finding log to the base 10 is like this:
sub log10 {
my $n=shift;
return log($n)/log(10);
}
So probably i am looking for a check which says if so and so value dont find log.
You cannot take the log of negative numbers.
See Wolfram MathWorld for more details.
Apart from the value being negative, the string -9.4351e+0.007 is not a valid number as the exponent part of a floating-point constant can be only an integer.
You must be passing strings to your log10 function as Perl would not complain about a number in this format.
You need to look at the source of these values as something is going wrong before your function is called, and it will probably give you incorrect results even for those values that can be passed to log without error.
"ln y" means "find the x where ex equals y".
e is a positive number (near 2.17828), so no matter how many times you multiply e with itself, you'll never get a negative number.
You cannot find the log of negative numbers.
As Borodin also points out that -9.4351e+0.007 isn't recognized as a number by Perl.
>perl -wE"say 0+'-9.4351e+0.007'"
Argument "-9.4351e+0.007" isn't numeric in addition (+) at -e line 1.
-9.4351

How does this Perl one liner to check if a directory is empty work?

I got this strange line of code today, it tells me 'empty' or 'not empty' depending on whether the CWD has any items (other than . and ..) in it.
I want to know how it works because it makes no sense to me.
perl -le 'print+(q=not =)[2==(()=<.* *>)].empty'
The bit I am interested in is <.* *>. I don't understand how it gets the names of all the files in the directory.
It's a golfed one-liner. The -e flag means to execute the rest of the command line as the program. The -l enables automatic line-end processing.
The <.* *> portion is a glob containing two patterns to expand: .* and *.
This portion
(q=not =)
is a list containing a single value -- the string "not". The q=...= is an alternate string delimiter, apparently used because the single-quote is being used to quote the one-liner.
The [...] portion is the subscript into that list. The value of the subscript will be either 0 (the value "not ") or 1 (nothing, which prints as the empty string) depending on the result of this comparison:
2 == (()=<.* *>)
There's a lot happening here. The comparison tests whether or not the glob returned a list of exactly two items (assumed to be . and ..) but how it does that is tricky. The inner parentheses denote an empty list. Assigning to this list puts the glob in list context so that it returns all the files in the directory. (In scalar context it would behave like an iterator and return only one at a time.) The assignment itself is evaluated in scalar context (being on the right hand side of the comparison) and therefore returns the number of elements assigned.
The leading + is to prevent Perl from parsing the list as arguments to print. The trailing .empty concatenates the string "empty" to whatever came out of the list (i.e. either "not " or the empty string).
<.* *>
is a glob consisting of two patterns: .* are all file names that start with . and * corresponds to all files (this is different than the usual DOS/Windows conventions).
(()=<.* *>)
evaluates the glob in list context, returning all the file names that match.
Then, the comparison with 2 puts it into scalar context so 2 is compared to the number of files returned. If that number is 2, then the only directory entries are . and .., period. ;-)
<.* *> means (glob(".*"), glob("*")). glob expands file patterns the same way the shell does.
I find that the B::Deparse module helps quite a bit in deciphering some stuff that throws off most programmers' eyes, such as the q=...= construct:
$ perl -MO=Deparse,-p,-q,-sC 2>/dev/null << EOF
> print+(q=not =)[2==(()=<.* *>)].empty
> EOF
use File::Glob ();
print((('not ')[(2 == (() = glob('.* *')))] . 'empty'));
Of course, this doesn't instantly produce "readable" code, but it surely converts some of the stumbling blocks.
The documentation for that feature is here. (Scroll near the end of the section)