writing file with output function haskell - file

I would like to write to a file the output of my program automatically, but some errors are appearing. I'm New to haskell.
A really simple example:
func = do
writeFile "file.txt" show(calc)
calc = do return (1+1)
I wanted something like this. When I execute the function, creating a file and write on it
The error is:
ERROR "test.hs":5 - Syntax error in input (unexpected `=')
Thanks.

There are several errors in this code:
You should remove the dos to simplify func and calc. Technically this is not an error but you should not use unnecessary dos because they just clutter up your code.
show has the type Show a => a -> String, whereas calc has the type IO String. Because the types mismatch, your program would not compile.
This is a program with the same functionality which compiles:
func :: IO () -- is an IO action
func = writeFile "file.txt" (show calc) -- removed the do
calc :: Integer -- has type integer, meaning "show" is able to operate on it
calc = 1+1 -- removed do and return
The reason to remove the return is that return does not behave like return in C-like languages. return in Haskell wraps a value in a monadic action whereas in C-like languages it just returns the value of an expression or a variable as result of a function. I would recommend reading this chapter for further information as it covers in- and output in Haskell pretty well.

Related

Read file and find index of value in array in Fortran

I am making user subroutine file of Abaqus.
However, in reading a file I met with a difficulty.
Since, it is Fortran 77 based, it is so hard to find exact solution.
My intention is to read a file where an 1X1 array is include. Then, to find an index of a value in the array.
My code to read a file is :
open (unit=99,file='D:\SIMULATION\dist.txt',status='old')
read (99,*) dist
close (99)
And the code for finding index of value in array is:
loc=minloc(abs(dist-1),1)
I think minloc is for Fortran 90, right?
Is there any function in Fortran 77 similar to minloc?
The code you've shown should compile and run as expected. I'm assuming that you are actually reading a 1xN array and that when you said "1X1" it was a typo - otherwise, there's no point in using minloc.
However, the error message you reported in a comment (An array-valued argument is required in this context) only occurs if you use the minloc intrinsic on a scalar value. Thus, my guess is that you did not declare dist as an array. Here is a quick example of what I mean:
! The contents of 'values.txt' are: -3.1, 4.1, 5.9, 2.6, -5.4
! Values may be separated by commas or blanks.
program get_min_dist
implicit none
real :: x ! <-- Cannot be used to represent an array.
real, dimension(5) :: a ! <-- An array of 5 reals. Do this instead.
integer :: loc, funit1
open(newunit=funit1, file="values.txt", status="old")
read(funit1,*) x
rewind(funit1)
read(funit1,*) a
close(funit1)
loc = minloc(abs(a-1),1) ! <-- I'm assuming there is a reason to
! subtract 1 from all values in the array
! loc = minloc(abs(x-1),1) ! <-- Error 'An array-valued arg is required`
print*, "x=",x
print*, "a=",a
print*, "index=", loc
print*, "value=", a(loc)
end program get_min_dist
With read(funit1,*) x the first value will be assigned when the file is read, resulting in the error message you have seen. With the array a, however, you get the expected output.
Your confusion regarding the need to use F77 compatible code may be due to the fact that Abaqus continues to provide examples and docs with F77-style fixed-formatting, and requires Fortran source code to be given the .f or .for extension1. By default, this extension tells ifort to expect fixed-format code2. However, any Fortran features supported by the version of the compiler you use is still valid - even in fixed-format, if you must. For further information about the availability of features from different Fortran versions, see your (Intel Fortran) documentation.
1 I'd be glad to know if this can be changed somehow, e.g. to allow the .f90 extension.
2 This setting can be changed in the Abaqus environment file, at least for the versions I've used (6.9-6.14). I don't think that has changed with newer releases, but maybe. I don't recommend changing it if you share the environment with other users without their consent, especially for newbies.

Calling C shared library function from LibreOffice Basic

I'm trying to call a C shared library function from LibreOffice Basic, but I always get "Basic Runtime Error. Not implemented" when it hits the Declare line. It's just for a fun thing but being unable to do it is bugging me.
The Declare statement looks like this:
Declare Function score_word Lib "libscrabblescore.so" (ByRef word As String, ByRef bonus As String) As Integer
The C function delaration looks like this:
int score_word(char* word, char* word_bonuses)
(Maybe ByRef word As String is not the right translation of char* word? I can't find documentation on how to use char* parameters to functions from LibreOffice Basic.)
I validated the shared library itself by calling it using Python's ctypes module:
>>> from ctypes import CDLL
>>> lib = CDLL("/usr/lib/libscrabblescore.so")
>>> lib.score_word("qi", "dw dlq")
42
>>>
(So I have the "Answer to the Ultimate Question of Life, the Universe, and Everything," just not for how to do this in LibreOffice Basic!)
I tried using the absolute path in the Declare statement as well, and it made no difference.
I found a Windows thread on the topic of calling DLL's where the asker said he needed to put the DLL in a specific location (the LibreOffice bin directory) so LibreOffice could access it. There is no LibreOffice bin directory per-se on Linux, and unfortunately there are 351 candidate directories I was able to identify on my machine (my path, and all folders with "libreoffice" in the name or under a folder with "libreoffice" in the name).
I tried a shotgun approach and put a symbolic link to the shared library in all 351 directories, but then Calc hangs on startup. So I removed those, started Calc, and put them all back in place and tried the function. If it was a location thing, you'd think that would work as LibreOffice Basic is supposed to load the library at the point of the Declare. Still no luck.
There was something that looked promising on oooforums but the site times out when I try to view the thread. (EDIT: I managed to view the thread this evening and it was Windows security problem. I turned off all macro security in my LibreOffice and still have the problem.)
So, has anybody ever successfully called a C shared library function from a LibreOffice Basic program that knows what I'm doing wrong? Thanks!
Jonathon Reinhart is correct; the "Declare" command for calling shared libraries is implemented on Windows but not on Linux. I was only able to verify this because of a reference to an OpenOffice bug report on the matter on a blog post.
My first attempt at a solution was to rewrite the function in LibreOffice Basic. It worked, but would take up to 3 seconds to return its results.
I considered giving up and leaving it like that, but it was too unpleasant to have a three second wait. I looked into how to implement a C++ function through UNO, which was overly complex for a fairly trivial task.
Finally what I did was to write a kludge that still gives "instant" results (around 0.025 seconds each function call).
I rewrote the DLL as a C++ console app that takes command line arguments and writes the result to a temp file. Then I replaced my LibreOffice Basic code with a function that calls the C++ console app in blocking mode using Shell and retrieves the results from the file. It's ugly, but it's not a multi-user thing and it works.
In case anyone stumbles across this issue themselves, here's the LibreOffice Basic code I used to do this--it's very simple.
option explicit
function scorewords(wordlist as string, bonuslist as string) as integer
dim cparams as string
dim fileno as integer
dim results_file as string
dim score as integer
if wordlist = "" then
scorewords = 0
exit function
end if
cparams = """" + wordlist + """" + " " + """" + bonuslist + """"
results_file = "/tmp/scrabblescore.dat"
Shell("/usr/bin/getscrabblescore", 6, cparams, true)
fileno = freefile
open results_file for input as fileno
input #fileno, score
close #fileno
kill results_file
scorewords = score
end function
Unless you are limited to Basic then it looks like your question already shows a good solution. Write something like this in Python UNO:
import ctypes
lib = ctypes.cdll.loadLibrary("/usr/lib/libscrabblescore.so")
result = lib.score_word("qi", "dw dlq")
oText.insertString(oTextCursor, result, 0)

fortran pass allocated array to main procedure

I have a module with a function which takes a starting and ending point and reads in a .txt some float value.
I wish that function to return a table which I do not know how large it will be before it starts.
I wish to use this function twice in the main program to make a third real array. But Fortran doesn't like it much.
Here is my code for the function :
module ReadData
!in this part, you need to know :
! -the starting (cannot be the first or second point)
! -end point
! -the file name (here : cham/comp or flow)
! change line 40 in case it is not AL026_Pd anymore
! -where it is on the file
implicit none
INTERFACE ReadP
MODULE PROCEDURE ReadDataPressure
END INTERFACE
private :: ReadDataPressure
contains
function ReadDataPressure (whereabout,StartingPoint,EndingPoint) result (P1)
!**********************
!**decla in variables**
character(50) :: whereabout !needed : cham/comp or flow
real(8) :: StartingPoint,EndingPoint
!************************
!**decla used variables**
character(50) :: FileNameConstructed
real(8) :: deltat,CurrentTime,pressure
integer(8) :: i,k
!**********************
!**decla out variable**
real(8),allocatable :: P1(:)
!start of the programe itself
write (FileNameConstructed,'(a,a,a)') "AL026_pd",whereabout,".txt"
open(20,file=FileNameConstructed,status='old',action='read')
read (20,*) deltat,pressure
read (20,*) CurrentTime,pressure
deltat=CurrentTime-deltat
!now deltaT is the loop counter, but we "lost" two usable line in the process
allocate (P1(1:int(((EndingPoint-StartingPoint)/deltat+1))))
k=1
do i=0,int((EndingPoint-2*deltat)/deltat)
read (20,*) CurrentTime,pressure
if (CurrentTime>StartingPoint) then
P1(k)=pressure
k=k+1
write(*,*) p1(k)
end if
end do
end function ReadDataPressure
End module
and I wish to do something like this in the main program
a=ReadP(comp,350,750)
b=ReadP(flow,350,750)
do i=1; lenght_of_a
m_ox(i)=squarreroot(a(i)-b(i))
end do
end then write it in another file.
I found : Share allocatable Arrays
FORTRAN - allocatable array in subroutine
but they did not help me.
One thinks perhaps
http://www.stanford.edu/class/me200c/tutorial_90/09_modules.html
is closer to the solution.
But they don't want a table at the end, they use Prod_A = PRODUCT(A) so you do not know the dimension of a, but can do product or sum. But I want to keep it whole.
In the main program you should be able to declare an allocatable array A and, if you have a Fortran 2003 compiler, do:
A = ReadDataPressure
Which is what you wish. This is allocation on assignment, which is part of Fortran 2003.
Why do you say that "fortran doesn't like it"? What are the specific error messages?
With compilers that don't support this it will be easier though less elegant to make the procedure a subroutine. Declare the array as allocatable in both the main program and the subroutine. Make it an intent (out) argument and allocate it in the subroutine.
P.S. Unless there are other aspects that you are not showing, setting up a module procedure for a single procedure seems pointless. I would leave off the interface and module procedure and make ReadDataPressure public so that it is directly called.
the mistake was :
character(50) :: whereabout
because there was
write (FileNameConstructed,'(a,a,a)') "AL026_pd",whereabout,".txt"
except that FileNameConstructed is also a character(50). (So i try to fit a 8+50+4 into 50).
But I was not able to see it before removing the private.
So thank MSB. You helped me a lot. I change whereabout in character(4) (since it feet my need perfectly) and so it is running

Rename file in Fortran 77

Is there a way to rename a file in fortran 77? such as:
RENAME(old name, new name)
or something like:
call system("rename" // trim(old name) // " " // trim(new name))
Thanks
I think you nailed it with the first one:
CALL RENAME('oldname','newname')
More here. And here.
You can use the modFileSys library for that. In contrast to non-standard compiler extensions, it can be compiled with any Fortran 2003 compiler and can be used an all POSIX compatible systems. You could also check for errors, if needed:
program test
use libmodfilesys_module
implicit none
integer :: error
! Renaming with error handling
call rename("old.dat", "new.dat", error=error)
if (error /= 0) then
print *, "Error happened"
end if
! Renaming without explicit error handling, stops the program
! if error happens.
call rename("old2.dat", "new2.dat")
end program test

Atomicity with openTempFile

I have the following function:
safeWrite :: Text -> IO ()
safeWrite c = bracket (openTempFile "/tmp" "list.tmp")
(\(path, h) -> hClose h
>> copyFile path dataFile
>> removeFile path)
(\(_, h) -> TI.hPutStr h c)
I was under the impression that this would safe, no copying would happen if there were errors during any moment, and the original file would still be usable. However just yesterday I ended up with an empty file, and I have no idea where to go look at it. The program had been running well for over a month without any hiccups which points so some corner case I didn't think of.
Does the method guarantee atomicity, meaning the error is somewhere else, or if not, why not? What should I do to guarantee atomicity?
Your definition of mkTemp is atomic with respect to Haskell exceptions. If there is an exception it will print a message about the failure (leaving the file there).
It is not atomic with respect to the Unix file system -- other programs could overwrite the same file
It does not clean up should there be a failure.
You can do a bit more to clean up, by optionally removing the file if there is an exception, or simply using the provided (atomic) mkTemp function:
openTempFile: http://hackage.haskell.org/packages/archive/base/4.3.1.0/doc/html/System-IO.html#g:22
or using the posix layer:
http://hackage.haskell.org/packages/archive/unix/latest/doc/html/System-Posix-Temp.html#Temp

Resources