From: Uno on
Richard Maine wrote:
> Uno <merrilljensen(a)q.com> wrote:

>> sqft = 144
>
> Oddly, I recall thinking of that line in your code as being particularly
> hard to read. The type promotion aside, I found the variable name
> confusing. Only by seeing how it was used in the code could I deduce
> that this was a conversion factor to turn square inches into square feet
> (by dividing).

It would have been better if it were
sqft = 144.0

Since I measure to the closest sixteenth, these numbers show up as a
rational number of inches, usually having a fractional part.

Most clients would be confused if I didn't do this small trick in
dimensional analysis. Dividing by a real is not something that
fortran's gonna drop the ball on.
--
Uno
From: Simon Geard on
On 20/04/2010 07:35, Uno wrote:
> Richard Maine wrote:
>> Uno <merrilljensen(a)q.com> wrote:
>> ...
>>> implicit integer (a-e)
>> ...
>>> q1) Does anything above look wrong?
>>
>> Well, one thing that strikes me as "wrong" (with the quotes because it
>> is not actually an error, but rather a stylistic choice) is the use of
>> implicit typing.
>>
>> While I have been known to make exceptions in the face of pleas of
>> hardship, I have a general policy of declining to help debug code that
>> uses implicit typing. It makes the work of debugging substantially
>> harder, as well as substantially increasing the number of bugs typically
>> present. I figure that if I spend the extra effort to debug the messes
>> that sometimes result, I am acting as an "enabler" in the sense of (from
>> Merriam Webster online)
>>
>> "one who enables another to persist in self-destructive behavior (as
>> substance abuse) by providing excuses or by making it possible to
>> avoid the consequences of such behavior"
>>
>> Some people have been known to argue that implicit typing simplifies
>> things for them enough to make up for any increased debugging problems.
>> That position seems incompatible with asking for debugging help.
>>
>> In this case, I stopped reading the code after seeing the IMPLICIT
>> statement.
>>
>
> That's a reasonable reaction. The only variable that ended up in the
> (a-e) range was 'cases'. It wou .. hold on,
>
> $ gfortran -Wall -Wextra m1.f90 -o out
> $ ./out
> wood supply is 672.00000
> living 258.86633
> dining 177.88020
> hall 28.285591
> sunroom 53.108505
> strip 23.138889
>
> total1 is 541.27954
> office 146.99132
> foyer 44.358074
> total2 is 732.62891
> $ cat m1.f90
> implicit real (a-z)
>
> sqft = 144
>
> cases = 28
> footage = 24
> supply = cases * footage
> print *, "wood supply is ", supply
>
> hor = 208.25
> vert = 179
> r1 = hor * vert / sqft
> print *, "living ", r1
>
> hor = 208.25
> vert = 123
> r2 = hor * vert / sqft
> print *, "dining ", r2
>
> hor = 85.75
> vert = 47.5
> r3 = hor * vert / sqft
> print *, "hall ", r3
>
> hor = 79.25
> vert = 96.5
> r4 = hor * vert / sqft
> print *, "sunroom ", r4
>
> hor = 208.25
> vert = 16
> r5 = hor * vert / sqft
> print *, "strip ", r5
>
> total1 = r1+r2+r3+r4+r5
>
> print *, " "
> print *, "total1 is ", total1
>
> hor = 118.25
> vert = 179
> r6 = hor * vert / sqft
> print *, "office ", r6
>
> hor = 40.75
> vert = 156.75
> r7 = hor * vert / sqft
> print *, "foyer ", r7
>
> total2 = total1+r6+r7
> print *, "total2 is ", total2
>
> end program
> ! gfortran -Wall -Wextra m1.f90 -o out
> $
>
> Better?

I think what Richard meant was that you should start with 'implicit
none' then explicitly declare each variable, e.g.

implicit none
real :: sqft, footage ! etc.
integer :: cases ! etc.


Simon
From: glen herrmannsfeldt on
Richard Maine <nospam(a)see.signature> wrote:
> Ron Shepard <ron-shepard(a)NOSPAM.comcast.net> wrote:
(snip)

>> Not necessarily. If you need something that is in fortran, but not
>> in excel or some other language, then you would use fortran. A good
>> example of this is eigenvalue problems. Excel doesn't have any
>> eigenvalue routines built-in. I sometimes wish that it did, but it
>> doesn't.

> But I think Glen's point (I sometimes misunderstand him, but I think I
> got this one - probably means I'm wrong :-)) was that you wouldn't write
> Fortran code to solve a particular Eigenvalue problem. Instead, you
> would write (or more likely, use one from a library) a routine that
> solved a more general Eigenvalue problem, and you would feed it the
> particular array in question (assuming one was talking array
> eigenvalues).

Yes.

In some other languages, one might feed a particular array,
though with the usual ability to change that array.

Not that I use Excel that much, but that would fit with the way
Excel is used. The Mathematica notebook also has a similar idea
about combining code and modifiable data.

As for Excel, one could write (or translate from Fortran) an
eigenvalue routine in VBA for use from Excel.

-- glen

From: Uno on
dpb wrote:

[big snips, a lot of code]

> What would have been better was a descriptive name for the conversion
> factor that relates to how is is used was the point being made. Again,
> in trivial code it's not terribly difficult to dig thru and retrieve
> such things from context altho it takes time and effort. As code size
> and complexity grows, the importance grows with it and it's quite
> possible (actually quite common) that even the original coder will
> puzzle over "what was I doing there????" when re-visiting code later if
> it isn't made patently clear thru naming conventions and/or comments or
> straightforward coding what was being implemented.

Thanks for your response, dpb. I think my latest version addresses this
criticism.
>
> You seem oblivious to the underlying issues raised re: IMPLICIT NONE --
> whether it's simply inexperience showing or stubbornness for the sake of
> maintaining present prejudices and practice it doesn't bode well for
> ease in debugging and code maintenance down the road...

Well, dpb, I figure that bugs are only as bad as wasps, and when I'm
getting my first number, I'm gonna get stung a couple times.

I'm aware that implicit none enforces strict typing, and have been so
demonstrably for twenty years. I won't back off the fact that the
ultimate calculation is a real. That might have been a good comment to
add at the git-go.

I'd rather talk about subsequent versions of this program:

$ pwd
/home/dan/source/fortran_stuff
$ ls
b.f90 directory.mod dw3.f90 fortran_resources.pdf m3.f90 m3.f90~ out
$ gfortran -Wall -Wextra m3.f90 -o out
$ ./out

ernest 1.00000000 2.0000000
parent: forest_root
children: douglas helen
douglas 6.0000000 7.0000000
parent: ernest
helen 3.0000000 4.0000000 5.0000000
parent: ernest
children: john
john 8.0000000
parent: helen

ernest 1.00000000 2.0000000
parent: forest_root
children: douglas helen
douglas 6.0000000 7.0000000
parent: ernest
helen 3.0000000 4.0000000 5.0000000
parent: ernest
children: betty john
betty 9.0000000 10.0000000
parent: helen
children: nigel peter ruth
nigel 11.000000
parent: betty
peter 12.000000
parent: betty
ruth
parent: betty
john 8.0000000
parent: helen

ernest 1.00000000 2.0000000
parent: forest_root
children: douglas helen
douglas 6.0000000 7.0000000
parent: ernest
helen 3.0000000 4.0000000 5.0000000
parent: ernest
children: betty
betty 9.0000000 10.0000000
parent: helen
children: nigel peter ruth
nigel 11.000000
parent: betty
peter 12.000000
parent: betty
ruth
parent: betty

ernest 1.00000000 2.0000000
parent: forest_root
children: douglas helen
douglas 6.0000000 7.0000000
parent: ernest
helen 3.0000000 4.0000000 5.0000000
parent: ernest
children: john betty
john 8.0000000
parent: helen
betty 9.0000000 10.0000000
parent: helen
children: nigel peter ruth
nigel 11.000000
parent: betty
peter 12.000000
parent: betty
ruth
parent: betty
$ cat m3.f90
! (c) Copyright Michael Metcalf and John Reid, 1992. This file may be
! freely used and copied for educational purposes provided this notice
! remains attached. Extracted from "Fortran 90 Explained" Oxford
! University Press (Oxford and New York), ISBN 0-19-853772-7.
!
!A recurring problem in computing is the need to manipulate a linked
!data structure.
!This might be a simple linked list like the one encountered in Section
!2.13, but often a more general tree structure is required.
!
!The example in this Appendix consists of a module that establishes and
!navigates one or more such trees, organized as a 'forest', and a short
!test program for it. Here, each node is identified by a name and has
!any number of children, any number of siblings, and (optionally) some
!associated real data. Each root node is regarded as having a common
!parent, the 'forest root' node, whose name is 'forest root'. Thus,
!every node has a parent. The module provides facilities for adding a
!named node to a specified parent, for enquiring about all the nodes
!that are offspring of a specified node, for removing a tree or subtree,
!and for performing I/O operations on a tree or subtree.
!
!The user-callable interfaces are:
!
!start:
! must be called to initialize a forest.
!add_node:
! stores the data provided at the node whose parent is specified
! and sets up pointers to the parent and siblings (if any).
!remove_node:
! deallocate all the storage occupied by a complete tree or
! subtree.
!retrieve:
! retrieves the data stored at a specified node and the names of
! the parent and children.
!dump_tree:
! write a complete tree or subtree.
!restore_tree:
! read a complete tree or subtree.
!finish:
! deallocate all the storage occupied by all the trees of the forest.
!
module directory
!
! Strong typing imposed
implicit none
!
! Only subroutine interfaces, the length of the character
! component, and the I/O unit number are public
private
public start, add_node, remove_node, retrieve,
&
dump_tree, restore_tree, finish
!
! Module constants
character(*), parameter:: eot = 'End-of-Tree.....'
integer, parameter, public :: unit = 4, & ! I/O unit number
max_char = 16 ! length of character
component
!
! Define the basic tree type
type node
character(max_char) :: name ! name of node
real, pointer :: y(:) ! stored real data
type(node), pointer :: parent ! parent node
type(node), pointer :: sibling ! next sibling node
type(node), pointer :: child ! first child node
end type node
!
! Module variables
type(node), pointer :: current ! current node
type(node), pointer :: forest_root ! the root of the forest
integer :: max_data ! max size of data array
character(max_char), allocatable, target :: names(:)
! for returning list of names
! The module procedures

contains

subroutine start
! Initialize the tree.
allocate (forest_root)
current => forest_root
forest_root%name = 'forest_root'
nullify(forest_root%parent, forest_root%sibling, forest_root%child)
allocate(forest_root%y(0))
max_data = 0
allocate (names(0))
end subroutine start
subroutine find(name)
character(*), intent(in) :: name
! Make the module variable current point to the node with given name,
! or be null if the name is not there.
type(node), pointer :: root
! For efficiency, we search the tree rooted at current, and if this
! fails try its parent and so on until the forest root is reached.
if (associated(current)) then
root => current
nullify (current)
else
root => forest_root
end if
do
call look(root)
if (associated(current)) return
root => root%parent
if (.not.associated(root)) exit
end do
contains
recursive subroutine look(root)
type(node), pointer :: root
! Look for name in the tree rooted at root. If found, make the
! module variable current point to the node
type(node), pointer :: child
!
if (root%name == name) then
current => root
else
child => root%child
do
if (.not.associated(child)) exit
call look(child)
if (associated(current)) return
child => child%sibling
end do
end if
end subroutine look
end subroutine find
subroutine add_node(name, name_of_parent, data)
character(*), intent(in) :: name, name_of_parent
! For a root, name = ''
real, intent(in), optional :: data(:)
! Allocate a new tree node of type node, store the given name and
! data there, set pointers to the parent and to its next sibling
! (if any). If the parent is not found, the new node is treated as
! a root. It is assumed that the node is not already present in the
! forest.
type(node), pointer :: new_node
!
allocate (new_node)
new_node%name = name
if (present(data)) then
allocate(new_node%y(size(data)))
new_node%y = data
max_data = max(max_data, size(data))
else
allocate(new_node%y(0))
end if
!
! If name of parent is not null, search for it. If not found, print message.
if (name_of_parent == '') then
current => forest_root
else
call find (name_of_parent)
if (.not.associated(current)) then
print *, 'no parent ', name_of_parent, ' found for ', name
current => forest_root
end if
end if
new_node%parent => current
new_node%sibling => current%child
current%child => new_node
nullify(new_node%child)
end subroutine add_node

subroutine remove_node(name)
character(*), intent(in) :: name
! Remove node and the subtree rooted on it (if any),
! deallocating associated pointer targets.
type(node), pointer :: parent, child, sibling
!
call find (name)
if (associated(current)) then
parent => current%parent
child => parent%child
if (.not.associated(child, current)) then
! Make it the first child, looping through the siblings to find it
! and resetting the links
parent%child => current
sibling => child
do
if (associated (sibling%sibling, current)) exit
sibling => sibling%sibling
end do
sibling%sibling => current%sibling
current%sibling => child
end if
call remove(current)
end if
end subroutine remove_node
recursive subroutine remove (old_node)
! Remove a first child node and the subtree rooted on it (if any),
! deallocating associated pointer targets.
type(node), pointer :: old_node
type(node), pointer :: child, sibling
!
child => old_node%child
do
if (.not.associated(child)) exit
sibling => child%sibling
call remove(child)
child => sibling
end do
! remove leaf node
if (associated(old_node%parent)) old_node%parent%child =>
old_node%sibling
deallocate (old_node%y)
deallocate (old_node)
end subroutine remove

subroutine retrieve(name, data, parent, children)
character(*), intent(in) :: name
real, pointer :: data(:)
character(max_char), intent(out) :: parent
character(max_char), pointer :: children(:)
! Returns a pointer to the data at the node, the name of the
! parent, and a pointer to the names of the children.
integer count, i
type(node), pointer :: child
!
call find (name)
if (associated(current)) then
data => current%y
parent = current%parent%name
! count the number of children
count = 0
child => current%child
do
if (.not.associated(child)) exit
count = count + 1
child => child%sibling
end do
deallocate (names)
allocate (names(count))
! and store their names
children => names
child => current%child
do i = 1, count
children(i) = child%name
child => child%sibling
end do
else
nullify(data)
parent = ''
nullify(children)
end if
end subroutine retrieve
subroutine dump_tree(root)
character(*), intent(in) :: root
! Write out a complete tree followed by an end-of-tree record
! unformatted on the file unit.
call find (root)
if (associated(current)) then
call out(current)
end if
write(unit) eot, 0, eot
contains
recursive subroutine out(root)
! Traverse a complete tree or subtree, writing out its contents
type(node), intent(in) :: root ! root node of tree
! Local variable
type(node), pointer :: child
!
write(unit) root%name, size(root%y), root%y, root%parent%name
child => root%child
do
if (.not.associated(child)) exit
call out (child)
child => child%sibling
end do
end subroutine out
end subroutine dump_tree

subroutine restore_tree
! Reads a subtree unformatted from the file unit.
character(max_char) :: name
integer length_y
real, allocatable :: y(:)
character(max_char) :: name_of_parent
!
allocate(y(max_data))
do
read (unit) name, length_y, y(:length_y), name_of_parent
if (name == eot) exit
call add_node( name, name_of_parent, y(:length_y) )
end do
deallocate(y)
end subroutine restore_tree

subroutine finish
! Deallocate all allocated targets.
call remove (forest_root)
deallocate(names)
end subroutine finish

end module directory
program test
use directory
implicit none
!
! Initialize a tree
call start
! Fill it with some data
call add_node('ernest','',(/1.,2./))
call add_node('helen','ernest',(/3.,4.,5./))
call add_node('douglas','ernest',(/6.,7./))
call add_node('john','helen',(/8./))
call add_node('betty','helen',(/9.,10./))
call add_node('nigel','betty',(/11./))
call add_node('peter','betty',(/12./))
call add_node('ruth','betty')
! Manipulate subtrees
open(unit, form='unformatted', status='scratch')
call dump_tree('betty')
call remove_node('betty')
write(*,*); call print_tree('ernest')
rewind unit
call restore_tree
rewind unit
write(*,*); call print_tree('ernest')
call dump_tree('john')
call remove_node('john')
write(*,*); call print_tree('ernest')
rewind unit
call restore_tree
write(*,*); call print_tree('ernest')
! Return storage
call finish

contains

recursive subroutine print_tree(name)
! To print the data contained in a subtree
character(*) :: name
integer i
real, pointer :: data(:)
character(max_char) parent, self
character(max_char), pointer :: children(:)
character(max_char), allocatable :: siblings(:)
!
call retrieve(name, data, parent, children)
if (.not.associated(data)) return
self = name; write(*,*) self, data
write(*,*) ' parent: ', parent
if (size(children) > 0 ) write(*,*) ' children: ', children
allocate(siblings(size(children)))
siblings = children
do i = 1, size(children)
call print_tree(siblings(i))
end do
end subroutine print_tree

end program test
! gfortran -Wall -Wextra m3.f90 -o out
$

So, how do I populate nodes with a room?
--
Uno



From: Richard Maine on
Uno <merrilljensen(a)q.com> wrote:

> dpb wrote:

> > You seem oblivious to the underlying issues raised re: IMPLICIT NONE
....
> I'm aware that implicit none enforces strict typing,

Actually, no, that is *NOT* the point being made. If that's what you
think it is about, then you are still missing it. The type aspect is one
part, but in many ways not the most important part. As Louis said

>>> The whole idea behind 'implicit none' is to force you to declare
>>> variables so that if you misspell something, the compiler catches it
>>> for you:

and as I tried to elaborate

>>> I don't think you understood Louis's comment - or my prior one. The
>>> problem Louis refers to applies to *ANY* implicit typing. Having all
>>> the types be real lowers the chance of being confused about what
>>> type something is, but it does *NOTHING* to avoid the problem that
>>> Louis refers to or many others of its ilk.

but the message doesn't appear to be getting through.

The most important part about implicit none for the current purposes is
*NOT* that it makes you declare what type each variable is. Yes,
implicit none does that, but that's not the most important part here.
The important part is that implicit none makes you declare what names
you are using for variables. That is what helps avoid the typographical
errors. It is perhaps unfortunate coincidence of terms that a "typing
error" could refer either to an error relating to data type or a
typographical error. Those are entirely unrelated problems, except that
the same words can be used in both contexts and implicit none can relate
to both. But the errors I am talking about are of the typographical
variety, which has nothing to do with strict typing.

--
Richard Maine | Good judgment comes from experience;
email: last name at domain . net | experience comes from bad judgment.
domain: summertriangle | -- Mark Twain