Using an implied do-loop to initialize a character array - fortran

Can I replace an explicit initialization of a character array, like this:
character(len=8), dimension(5) :: names = (/'ens_0001','ens_0002','ens_0003','ens_0004','ens_0005'/)
with an implied do-loop construct? Something with a formatted write statement?
character(len=8), dimension(5) :: names = write? other magic? 'ens_',i4.4', ... (/j=1,5/)

The question is essentially equivalent to the alternative question: can I convert an integer to a character in a constant expression?
Yes you can, but you are limited in how this can be done.
One way would be:
implicit none
character, parameter :: digit(0:*) = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
integer i
character :: number(3) = [(digit(i), i=2, 8, 3)]
print '(A)', number
end
And there are other ways, such as using ACHAR.
Once you know how to get a single character you can use the natural extensions, such as
character(8) :: label(5) = 'ens_000'//[(digit(i), i=1, 5)]
And so on, extending to tens, hundreds and more if you find the appropriate logic:
integer j
character(2), parameter :: twodigit(0:*) = [((digit(i)//digit(j), j=0,9), i=0,9)]
character(8) :: label(12) = 'ens_0'//[(twodigit(i), i=11, 22)]
You cannot use an internal write (or other executable statement) or a user-defined function in the constant expression.
This does quickly become silly as the number of digits increases, but it's also possible to use indexes (or substrings) and arithmetic:
character(8) :: label(12) = 'ens_0'//[(digit(i/10)//digit(mod(i,10)), i=11, 22)]
(Recall if using them that substrings are always 1-indexed.)

Thanks Francescalus for the pointers! That really helped. The final solution is posted below. I never actually stated this detail, but I had to go from ens_0001 to ens_1200, so the nested implicit do-loops are 4 deep!
program foo
implicit none
integer i,j,k,l
character, parameter :: digit(0:*) = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
character(4), parameter :: fourdigit(0:*) = [((((digit(i)//digit(j)//digit(k)//digit(l), l=0,9), k=0,9), j=0,9), i=0,9)]
character(8) :: label(1200) = 'ens_'//[(fourdigit(i), i=1, 1200)]
print '(A)', label
end

Related

Is there a function that removes all nonalphanumeric numbers in Fortran?

New to Fortran,been trying to think of a function that replaces all non alphanumeric characters and spaces on a string so that it turns something like [AS:1] to AS1.
Anyone here got a clue how to?
Like I got a trimmer for open spaces to work but I don't know how to make it work for anything that's a non-alphanumeric character.
The intrinsic function SCANcan be used for membership searches.
If we have a character char of length-1 and a set set of non-zero size, then we have that SCAN(char, set) returns 1 (0) if char is in (not in) the set. (SCAN will return 0 if the set is of size zero.)
This functions is elemental so, for example, SCAN(char_array, set) returns an indicator for which elements of char_array are in the set.
We also have PACK which returns another array corresponding to a selection mask:
print*, PACK(char_array, SCAN(char_array,set).eq.1)
Which means we can write a subroutine like
subroutine s(in, out, keep, len)
integer, intent(in) :: len
character, intent(in) :: in(len), keep*(*)
character, intent(out) :: out(len)
integer :: i
out = PACK(in, SCAN(in,keep).eq.1, [(' ',i=1,len)])
end subroutine s
taking an input character array of size len and returning an output character array of the same size with the elements which are in keep (and trailing elements being blanks).
Naturally, we don't like working with character arrays instead of scalars, so let's provide a nice subroutine using sequence association:
subroutine strip(in, out, keep)
character(*), intent(in) :: in, keep
character(*), intent(out) :: out
call s(in, out, keep, LEN(in))
end subroutine
Complete example:
module stripping
implicit none
private s
contains
subroutine strip(in, out, keep)
character(*), intent(in) :: in, keep
character(*), intent(out) :: out
call s(in, out, keep, len(in))
end subroutine strip
subroutine s(in, out, keep, len)
integer, intent(in) :: len
character, intent(in) :: in(len), keep*(*)
character, intent(out) :: out(len)
integer :: i
out = PACK(in, SCAN(in,keep).eq.1, [(' ',i=1,len)])
end subroutine s
end module stripping
program test
use stripping, only : strip
implicit none
character(10) in, out
character(*), parameter :: keep="abcd"
in = "a1b*2sdc]a"
call strip(in, out, keep)
print*, TRIM(out)
end program
There are doubtless better and clearer ways to do this: this answer mostly serves to have you think about what intrinsic functions there are and how they can be applied. There isn't an intrinsic function to do what you want in one step.
You need to define an external verification procedure that tells if a given character is to be kept or discarded. Then replace the equivalence check typically done in replace routines with this external function.
Here is an implementation that achieves the goal,
module str_mod
implicit none
integer, parameter :: IK = kind(0)
integer, parameter :: SK = kind("a")
integer, parameter :: LK = kind(.false.)
contains
! Returns `.true.` if it is a desired character.
function isDesired(char) result(desired)
character(1, SK), intent(in) :: char
logical(LK) :: desired
desired = (SK_"0" <= char .and. char <= SK_"9") .or. &
(SK_"A" <= char .and. char <= SK_"Z") .or. &
(SK_"a" <= char .and. char <= SK_"z")
end function
function replace(str, isDesired) result(strrep)
character(*, SK), intent(in) :: str
character(:, SK), allocatable :: strrep
procedure(logical(LK)) :: isDesired
integer(IK) :: i, counter
allocate(character(len(str), SK) :: strrep)
counter = 0_IK
do i = 1, len(str, kind = IK)
if (.not. isDesired(str(i:i))) cycle
counter = counter + 1_IK
strrep(counter:counter) = str(i:i)
end do
strrep = strrep(1:counter)
end function
end module str_mod
use str_mod
print *, replace("Fortran", isDesired)
print *, replace("(Fortran)", isDesired)
print *, replace("(Fortran) (Is) [_A_] (GREAT) {language}.", isDesired)
print *, replace("[AS:1]", isDesired)
end
Here is the program output,
Fortran
Fortran
FortranIsAGREATlanguage
AS1
Test it here. Note that this implementation performs two allocations of the output strings, which you could likely avoid by counting the desired characters in str first and then allocating the output string to the proper size and filling it with the identified characters. But any performance gain or difference will likely be negligible in most scenarios. You would likely see better performance benefits if you instead reimplement replace() in the above as a subroutine with str input argument being an allocatable with intent(inout). In such a case, you can avoid an extra copy on exit from the procedure, which can lead to ~25% runtime speedup for small arrays. But again, such performance concerns become relevant only when you call replace() on the order of billions of times.
You would have to write a function to do it. As inspiration, here's a subroutine I recently wrote to do SQL "escaping" of quotes in a string. The key here is having separate indexes for input and output position. Your requirement is even easier - if the character is not alphanumeric or space, don't advance the output length. There are several ways of doing the comparison, an exercise left for the reader.
subroutine escape (text)
character(*), intent(inout) :: text
character(100) :: newtext
integer i,j
newtext = ' '
j = 1
do i=1,len_trim(text)
if (text(i:i) == '"') then
newtext(j:j) = "\"
j = j + 1
end if
newtext(j:j) = text(i:i)
j = j + 1
end do
text = newtext
end subroutine escape

Function in Clojure that takes a list of n letters and returns a list

I need your help to write a function that takes a list of letters like ['a', 'b', 'c'] and returns another list of letters like ['a', 'b', 'b', 'c', 'c', 'c'], this is the first element is repeated only once the second element is repeated two times, the third element is repeated three times, etc. Thank you in advance!.
(mapcat repeat (map inc (range)) ["a" "b" "c"])

Fortran 90: Repeated elements of a string

I want to write a program that tells me how many times appears each letter of a character of some dimension n. For example:
Character(length=4) :: char="hello"
The program must give me:
'H appeared: ' 1 'time'
'E appeared: ' 1 'time'
'L appeared: ' 2 'times'
'O appeared: ' 1 'time'
Here's picture of what I've done, But of course it has some errors and idk how to fix it. For instance, i want it to print each letter only once, but because the write(,) is inside the first 'do i=...' each n-repeated letter is printed n times.
enter image description here
First of all after looking at you code, I would suggest using IMPLICIT none. Also you have a few redundant variables which I have attempted to remove.
Also, in order to ensure each character is processed only once (which is your question), you need to have an additional do loop to check whether the next character string has already been processed. I have include a possible answer below, though have turned off the IMPLICIT feature but tried to keep in line with your coding style:
PROGRAM MAIN
IMPLICIT NONE
INTEGER :: N,I,J
CHARACTER*8 CHAAR
LOGICAL PROCCHAR
CHAAR = "homework"
DO I=1,8
N=1
C **** FIRST CHECK THIS CHARACTER HAS NOT BEEN PROCESSED ALREADY
PROCCHAR = .TRUE.
DO J=1,i-1
IF(CHAAR(I:I) == CHAAR(J:J)) THEN
PROCCHAR = .FALSE.
ENDIF
ENDDO
C **** LOOK THROUGH REST OF CHARACTER STRING. BUT ONLY IF THIS
C CHARACTER HAS NOT BEEN PROCESSED SO FAR
DO J=i+1,8
IF(CHAAR(I:I) == CHAAR(J:J).AND.PROCCHAR) THEN
C AUX = CHAAR(J:J)
N = N + 1
ENDIF
ENDDO
IF(PROCCHAR) WRITE(*,*) 'CHARAACTER ',CHAAR(I:I),' OCCURS ',N
ENDDO
END
SCAN, INDEX, VERIFY are the most notable Fortran intrinsic functions dealing with character search in strings. For example, this is a case-insensitive working implementation:
program test_hello
implicit none
character(len=*), parameter :: upperAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
character(len=*), parameter :: lowerAlphabet = "abcdefghijklmnopqrstuvwxyz"
call printLettersInWord("hello")
call printLettersInWord("fortran")
contains
subroutine printLettersInWord(word)
character(len=*), intent(in) :: word
integer :: j,letterCount(0:len(upperAlphabet))
if (len_trim(word)<=0) return
print *, 'In word <',word,'>:'
letterCount = countLetters(word)
do j=1,len(upperAlphabet)
if (letterCount(j)<=0) cycle
print "(' letter ',a,' found ',i0,' times ')", upperAlphabet(j:j),letterCount(j)
end do
end subroutine printLettersInWord
pure function countLetters(word) result(letterCount)
character(len=*), intent(in) :: word
integer :: letterCount(0:len(upperAlphabet))
integer :: i,thisLetter
letterCount = 0
do i=1,len(word)
thisLetter = max(index(upperAlphabet,word(i:i)),index(lowerAlphabet,word(i:i)))
letterCount(thisLetter) = letterCount(thisLetter)+1
end do
end function countLetters
end program test_hello
That produces the following output:
In word <hello>:
letter E found 1 times
letter H found 1 times
letter L found 2 times
letter O found 1 times
In word <fortran>:
letter A found 1 times
letter F found 1 times
letter N found 1 times
letter O found 1 times
letter R found 2 times
letter T found 1 times

Ocaml: Get a list of characters that are between two characters

to clarify my dilemma I'll explain the problem I'm faced with...
Basically, I am being passed a string that can contain single characters or ranges of characters and am trying to return back a list of characters represented by the string I was passed.
Ex. "b" would just give a list ['b'] "a-z" would give ['a' ; 'b' ; 'c' ; ... ; 'z'] and something like "ad-g2-6" would be ['a' ; 'd' ; 'e' ; 'f' ; 'g' ; '2' ; '3' ; '4' ; '5' ; '6'] since there is the character a and the ranges d-g and 2-6. (Also worth noting that something like "a-" would just be ['a' ; '-'] since the range wasn't completed.
My ideas for solving this have come to exploding the string into a list of characters (lst) then pattern matching and building onto an accumulator like
let mainfunc str = let lst = (explode str) in
let rec func lst acc = match lst with
| [] -> acc
| a::'-'::b::t -> func t (acc # **SOMETHING TO GET THIS RANGE**)
| a::t -> func t (acc # [a])
in func lst []
Anything that could help me get a range between the characters would be great and I'm open to ideas if someone has a better way to go about this problem than what I have set up.
(Also note that my explode function works as intended and converts a string into a char list)
Since you wrote a successful explode function I'll assume that you have no trouble with recursion etc. So the problem might just be a way to talk about characters as values (so you can get the next character after a given one).
For this you can use Char.code and Char.chr (from the OCaml standard library).
Here's a function that takes a character and returns a list consisting of the character and the next one in order:
let char_pair c =
[ c; Char.chr (Char.code c + 1) ]
Here's how it looks when you run it:
# char_pair 'x';;
- : char list = ['x'; 'y']
(I leave as an exercise the problem of dealing with the character with code 255.)
As a side comment, your approach looks pretty good to me. It looks like it will work.

How can I name elements in a list and replace them?

I have two list and I want to check if listb is subordinate to lista
a = [['1', '2'], ['2', '3'], ['1', '2', '3']]
b = ['2', '1']
So I tried following code:
for element in b:
checklist = []
parts = element.split(',')
for x in a:
if set(b).issubset(set(x)) is True:
check.append(x)
this works! but it will output:
[['1', '2'], ['1', '2', '3']]
what I want is make output like:
[1, 3] or [a, c] -----------1 and a stand for['1', '2'];3 and c stand for['1', '2', '3']
because this is only a short example here(I got a more complex a in fact...), so I tend to use loop to make it work...any idea? Thanks!
I work it out following code hope can help:
nodeNo = []
for value in checklist:
for position, item in enumerate(a):
if item == value:
nodeNo.append(position+1)
list2 = ['NO{0}'.format(i) for i in nodeNo]
print list2
Hope can help who need it. thanks anyway