Haskell - syntax in do blocks (using IO) - if-statement

The compiler says
The last statement in a 'do' construct must be an expression:
rmax <- getInteger
when attempting to load a file containing the following snippets of code:
getInteger :: IO Integer
getInteger = readLn
main :: IO ()
main = do
putStrLn "specify upper limit of results"
rmax <- getInteger
if rmax `notElem` mot
then do putStrLn "run again and enter a multiple of 10"
else do print pAllSorted
What does it (the compiler message) mean, and why does it occur here? (whereas it doesn't in:)
main = do
line <- getLine
if null line
then return ()
else do
putStrLn $ reverseWords line
main
reverseWords :: String -> String
reverseWords = unwords . map reverse . words
(above example taken from http://learnyouahaskell.com/input-and-output)

Your indentation is probably messed up because of mixed tabs and spaces. In fact, there appears to be a stray tab in the code snippet in your question, which I'm assuming you pasted directly from your source file.
Most likely, GHC is interpreting the tabs differently from how your editor displays them, so it thinks the do block ends after the line in question.
As a rule of thumb, it's best to use only spaces in Haskell. The language defines very specific rules for interpreting tabs that most code editors don't agree with, but spaces are unambiguous and consistent.

Related

How to differentiate between space and tab while performing file operations in ocaml

I have a dumped(.rem) file with 3 entries per line, separated by tabs - "\t" as shown below.
Hello World Ocaml
I like Ocaml
To read from this file, the type is passed in a cast(attrbs) along with the file like this:
type attrbs = list (string * string * string);
let chi = (open_in file : attrbs) in
let v = input_value chi in close_in chi
Now, I get a list in "v", which I use further. In fact, it also works if the entries are separated by space.
This works fine if all the 3 entries in a row do not contain any spaces within themselves. I would like to use another file which has the first entry as a string with spaces, second entry as a string without spaces, and third entry as any string as shown below:
This is with spaces Thisiswithoutspaces Thisissomestring
Another one with spaces Anotheronewithoutspaces AnotherString
If I use the code mentioned, since it does not differentiate between space and tab, it takes only the first three words - "This", "is", and "with". I want it to include the spaces and consider "This is with spaces" as an entire string.
I tried searching the web, but couldn't find any solution for it.
Update:
The issue was with the way I read them. If I use specific formats like "%s %s %s", they will work only if we add the # character like "%s#\t%s#\t%s". It is given under the title: "Scanning indications in format strings" in https://caml.inria.fr/pub/docs/manual-ocaml/libref/Scanf.html. The issue is solved.
Glad you managed to do this yourself.
However, I wouldn't recommend using Scanf for that. You can do this:
match String.split_on_char '\t' (input_line chi) with
| [a;b;c] -> ...
| exception End_of_file -> ...
| l_wrong_size -> ...
This way, you are not only sure to not rely on the quirky behavior of Scanf, but you can also easily specify what to do on malformed input.
The issue was with the way I read them. If I use specific formats like "%s %s %s", they will work only if we add the # character like "%s#\t%s#\t%s". It is given under the title: "Scanning indications in format strings" in https://caml.inria.fr/pub/docs/manual-ocaml/libref/Scanf.html. The issue is solved.

Add blank space to a string when outputting in Fortran

I want to add blank space in a string. For example: name is a variable that equal to "abcxyzdefg".
Now, I want to print this string as: abc xyz defg
I used this program
program name_space
implicit none
character(len=30) :: name = "abcxyzdefg"
write(*,3) name
3 format (A3, 2X, A3, 2X, A4)
end program
I want output as:
abc xyz defg
But, I am getting by this way:
abc
You must refer to the appropriate substrings
write(*,3) name(1:3), name(4:6), name(7:)
with just doing write(*,'(A3,1X,A3,...)') name the first descriptor prints the first three characters of name and then the output list is finished, there are no more items to be printed, so the write statement terminates.
With the output statement
write(*,3) name
we are treating name as a single transfer item, processed by the A3 format. This A3 format prints the first three characters for the string.
There's no further processing that can be done which transforms the item to a desirable form.
Instead, we may want to have three different transfer items. One way is as in the answer by Vladimir F, to use individual substrings:
write(*,3) name(1:3), name(4:6), name(7:10)
write(*,3) (name(i:i+2), i=1,4,3), name(7:) ! Can use implied do if desired
We can also split name in some way to get an array (if the elements are the same length that we know). As an array, each element forms an individual output item:
write(*,3) TRANSFER(name, 'aaa', 3)
write(*,'(3A4)') split_into_chunks_of_4(name)
or we can transform the string to add spaces and then output that:
write(*,'(A)') split_into_chunks(name) ! for some suitable function
For the case of the question, Vladimir F's answer is the best approach. In other cases there are many options.
As the more general case of inserting spaces into a string hides much of the hard work, it's perhaps only fair to give an indication of an approach:
! Add spaces to a string str at the breakpoints bps
function split(str, bps)
character(*), intent(in) :: str
integer, intent(in) :: bps(:)
character(LEN(str)+SIZE(bps)) :: split
integer i
split=''
split(1:bps(1)-1) = str(1:bps(1)-1)
do i=1, SIZE(bps)-1
split(bps(i)+i:bps(i+1)-1+i) = str(bps(i):bps(i+1))
end do
split(bps(i)+SIZE(bps):) = str(bps(i):)
end function

Fortran Character Input at Undefined Length

program Test
implicit none
character (LEN=100) :: input
character (LEN=100) :: output
print *,"Please input your message: "
read *, input
For every character, I encrypt it in Ceaser's Cipher
Calculations
print *,"This is the output: "
write (*,"(2a)") "Message = ", out
end program Test
This doesn't work entirely.
For every character in the input, I convert it using the modulo(iachar()) functions. It works up until the print, I followed the debugging, the encryption is fine.
But the issue with the output lies in LEN=100. The do loop will go through 100 times converting nonexistent characters into garbage, breaking the program at output with UNDEFINED TYPE.
So if I input "test", it will encrypt CBNC*GARBAGE-TO-100* and not output. If I define length as 4, and do it, it works. but I want to be able to do it without defining a length. Any way around this?
The read statement should pad input out to the full length of the variable (100 characters) with blanks, rather than adding "garbage". The LEN_TRIM intrinsic function will give the significant length of the variable's value - i.e. the length excluding trailing blanks. You may need to remember this significant length of the input string for when you print the output string.
(Note the rules on list directed input (indicated by the * in the read statement) can be a little surprising - a format of "(A)" may be more robust, depending on the behaviour your want.)
In terms of avoiding fixed length strings in the context of reading input - Fortran 2003 introduces deferred length character, which greatly helps here. Otherwise see Reading a character string of unknown length for Fortran 95 possibilities. One complication is that you are reading from the console, so the backspace statement may not work. The work around to that follows a similar approach to that linked, but necessitates piecewise building the input string into an allocatable array of character at the same time as the input record length is being determined. Sequence association is then used to convert that array into a scalar of the right length. Comment or ask again if you want more details.
The following code reads a user input string of unspecified length. Be aware that it requires a compiler that supports deferred-length character strings: character(len = :). Deferred-length character strings were introduced in Fortran 2003.
program test
use iso_fortran_env, only : IOSTAT_EOR
implicit none
integer :: io_number
character(len = 1) :: buffer
character(len = :), allocatable :: input, output
input = ""
print *, "Please input your message."
do
read(unit = *, fmt = '(a)', advance = "no", iostat = io_number) buffer
select case (io_number)
case(0)
input = input // buffer
case(IOSTAT_EOR)
exit
end select
end do
allocate(character(len=(len(input))) :: output)
! Now use "input" and "output" with the ciphering subroutine/function.
end program test
Explanation
The idea is to read in a single character at a time while looking for the end-of-record (eor) condition. The eor condition is caused by the user pressing the "return" key. The "iostat" option can be used to look for eor. The value returned by "iostat" is equal to the integer constant "IOSTAT_EOR" located in the the module "iso_fortran_env":
use iso_fortran_env, only : IOSTAT_EOR
We declare a deferred-length character string to grab user input of an unknown length:
character(len = :), allocatable :: input
In the "read" statement, "advance = 'no'" allows a few characters to be read in at a time. The size of "buffer" determines the number of characters to be read in (1 in our case).
read(unit = *, fmt = '(a)', advance = "no", iostat = io_number) buffer
If "iostat" returns a "0", then there were no errors and no eor. In this case the "buffer" character should be added to the "input" string. Ultimately this step allocates a "new" input that has the size of the "old" input + the buffer character. The newly allocated input contains the characters from the old input + the buffer character.
select case (io_number)
case(0)
input = input // buffer
If "iostat" returns an eor value, then exit the do loop.
case(IOSTAT_EOR)
exit
The standard Fortran string is fixed length, padded on the right with blanks. If your input string will never have trailing blanks the solution is easy: use the Fortran intrinsic function len_trim to find the nonblank length of the string and process only those characters. Another approach is to use a new feature, allocatable string ... this provides variable length strings. If disallowing blanks at the end of the string is acceptable, you will probably find using len_trim easier.

Simulating range(L,N) in erlang

Early in the morning playing with Erlang I got a curious result:
-module(bucle01).
-compile(export_all).
for(N) when N >=0 ->
lists:seq(1,N).
for(L,N) when L =< N ->
lists:seq(L,N);
for(L,N) when L > N ->
lists:reverse(for(N,L)).
When I run the program I see this:
> bucle01:for(1,10).
[1,2,3,4,5,6,7,8,9,10]
> bucle01:for(10,1).
[10,9,8,7,6,5,4,3,2,1]
>bucle01:for(7,10).
[7,8,9,10]
>bucle01:for(8,10).
"\b\t\n" %% What's that !?!
>bucle01:for(10,8).
"\n\t\b" %% After all it has some logic !
Any "Kool-Aid" to "Don't drink too much" please ?
Strings in Erlang are just lists of ASCII numbers. The Erlang shell tries to determine, without metadata, if your list is a list of numbers or a string by looking for printable characters.
\b (backspace), \t (tab) and \n (newline) are all somewhat common ASCII characters and therefore the shell shows you the string instead of the numbers. The internal structure of the list is exactly the same, however.
This is also covered by the Erlang FAQ:
Why do lists of numbers get printed incorrectly?
And here's a few ideas to prevent this magic: Can I disable printing lists of small integers as strings in Erlang shell?

Haskell Regular Expressions and Reading String as Integer

Let's say I want to consider input of the form
[int_1, int_2, ..., int_n]
[int_1, int_2, ..., int_m]
...
where the input is read in from a text file. My goal is to obtain the maximum size of this list. Currently I have a regular expression that recognizes this pattern:
let input = "[1,2,3] [1,2,3,4,5]"
let p = input =~ "(\\[([0-9],)*[0-9]\\])" :: [[String]]
Output:
[["[1,2,3]","[1,2,3]","2,"],["[1,2,3,4,5]","[1,2,3,4,5]","4,"]]
So what I'm after is the max of the third index + 1. However, where I'm stuck is trying to consider this index as an int. For instance I can refer to the element just fine:
(p !! 0) !! 2
> "2,"
But I can't convert this to an int, I've tried
read( (p !! 0) !! 2)
However, this does not work despite the fact that
:t (p !! 0) !! 2
> (p !! 0) !! 2 :: String
Appears to be a string. Any advice as to why I can't read this as an int would be greatly appreciated.
Thanks again.
I'm not entirely sure that your approach is one I'd recommend, but I'm struggling to wrap my head around the goal, so I'll just answer the question.
The problem is that read "2," can't just produce an Int, because there's a leftover comma. You can use reads to get around this. reads produces a list of possible parses and the strings left over, so:
Prelude> (reads "2,") :: [(Int,String)]
[(2,",")]
In this case it's unambiguous, so you get one parse from which you can then pull out the int, although regard for your future self-respect suggests being defensive and not assuming that there will always be a valid parse (the Safe module is good for that sort of thing).
Alternatively, you could modify your regex to not include the comma in the matched group.