Best Implementation for Set of Elements that I manually change

New to FreeBASIC? Post your questions here.
JohnM
Posts: 6
Joined: Apr 17, 2019 13:29

Best Implementation for Set of Elements that I manually change

Postby JohnM » Apr 28, 2019 10:07

Sorry for that title. I am new to both the language and its concepts. The syntax I will eventually figure out, but I worry that I'm not implementing the idea in the most efficient way. So, any nudges in the right direction are helpful.

Broadly, I hope to construct a program that will generate a text output based in part on randomization.

As a first step, I want to have a string randomly chosen from a set of strings that I am continually adding to/subtracting while I think of more elements. So, imagine that I want to randomly generate a name. I might have a few in mind, but tomorrow or next week I may think of more.

At first I thought I'd just keep a list inside an initialization. But I realized there is likely an easier way if I could keep the list in a text file, so that I could simply open, press enter, add a name, and save.

I am really sorry for this:

dim array() as String
read currentline from file
for(i++, end of file){
array[i] = currentline
}
randomselect = randomize(i)
print array[randomselect]

Mostly I want to know if this is the most efficient way to implement the general idea.
MrSwiss
Posts: 3083
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Best Implementation for Set of Elements that I manually change

Postby MrSwiss » Apr 28, 2019 13:41

JohnM wrote:Mostly I want to know if this is the most efficient way to implement the general idea.

Yes, however, it is important to keep the code dynamic, for this to work.
The points to watch are:
  • dynamic array (as opposed to static)
  • dynamically sized loop(s) (as opposed to using literals)
This is simply conceptual ...

Code: Select all

Dim As String sa(Any)   ' dyn. array (not initialized)

' size sa(... To ...), depending on file's content (e.g. number of names)

For i As UInteger = LBound(sa) To UBound(sa)
   ...
   ...
Next

read currentline from file ' <-- this may be better split into 2 procedures (for code reusability)
    1) load all content from file (one shot operation)
    2) split string (returned from above), into its parts (lines here)
(2) This may also be used to size the used dyn. array, as well as to 'fill' it.

(1) called LoadFile:

Code: Select all

#Include "file.bi"                      ' needed by LoadFile()

Function LoadFile( _                    ' load entire file content, into a string
    ByRef file  As Const String _       ' filename to load (and Path, when required)
    ) As String                         ' file content (all of it)
    Dim As String   text                ' return variable (local)

    If FileExists(file) Then            ' only, if file found
        Var n = FreeFile(), txlen = FileLen(file)   ' file nmber | file size
   
        If Open(file, For Binary Access Read, As #n) = 0 Then
            If txlen > 0 Then           ' only, if NOT 0 byte file
                text = String(txlen, 0) ' assign string memory (otherwise, FAIL)
                Get #n,, text           ' load entire content (into string)
            End If
            Close(n)                    ' always: close opened file(s)
        Else
            text = file + ": open failed!"  ' display error 'open'
        End If
    Else
        text = file + ": not found!"    ' display error 'found' (file might _
    End If                              ' need prepended 'Path' information!

    Return text
End Function
dodicat
Posts: 5771
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Best Implementation for Set of Elements that I manually change

Postby dodicat » Apr 28, 2019 17:07

Freebasic doesn't rely on a plethora of libraries (includes e.t.c) as does say, Python or Freepascal.
Once you have made your own procedures to do tasks then they can be re-used.

Here are some which may suit your task.
Freebasic arrays are written in brackets () and are not restricted to zero based.
They are declared as a specific data type (string , integer, ...) and can be multi-dimensional.
But you need strings:
example

Code: Select all

#Include "file.bi"

Function StringSplit(s_in As String,chars As String,result() As String) As Long 'split a string on any of chars
    Dim As Long ctr,ctr2,k,n,LC=Len(chars)
    Dim As boolean tally(Len(s_in))
    #macro check_instring()
    n=0
    While n<Lc
        If chars[n]=s_in[k] Then
            tally(k)=true
            If (ctr2-1) Then ctr+=1
            ctr2=0
            Exit While
        End If
        n+=1
    Wend
    #endmacro
   
    #macro split()
    If tally(k) Then
        If (ctr2-1) Then ctr+=1:result(ctr)=Mid(s_in,k+2-ctr2,ctr2-1)
        ctr2=0
    End If
    #endmacro
    '==================  LOOP TWICE =======================
    For k  =0 To Len(s_in)-1
        ctr2+=1:check_instring()
    Next k
    If ctr Then Redim result(1 To ctr): ctr=0:ctr2=0 Else  Return 0
    For k  =0 To Len(s_in)-1
        ctr2+=1:split()
    Next k
    '===================== Last one ========================
    If ctr2>0 Then
        Redim Preserve result(1 To ctr+1)
        result(ctr+1)=Mid(s_in,k+1-ctr2,ctr2)
    End If
    Return Ubound(result)
End Function

Function savefile(filename As String,p As String) As String
    Dim As Long n=Freefile
    If Open (filename For Binary Access Write As #n)=0 Then
        Put #n,,p
        Close
    Else
        Print "Unable to save " + filename:Sleep:End
    End If
    Return filename
End Function

Function loadfile(file As String) As String
   If Fileexists(file)=0 Then Print file;" not found":Sleep:End
    Dim As Long  f=Freefile
    Open file For Binary Access Read As #f
    Dim As String text
    If Lof(f) > 0 Then
        text = String(Lof(f), 0)
        Get #f, , text
    End If
    Close #f
    Return text
End Function

Sub loadarray(filename As String,a() As String) 'file to array
    Dim As String ret=loadfile(filename)
    stringsplit(ret,Chr(13,10),a())
End Sub

Function pre_pend(filename As String,txt As String) As String 'optional
    Dim As String s=loadfile(filename)
    If fileexists(filename) Then savefile(filename,txt+s)
    Return filename
End Function

Function ap_pend(filename As String,txt As String) As String
    Dim As String s=loadfile(filename)
    If fileexists(filename) Then savefile(filename,s+txt)
    Return filename
End Function

Sub arraydelete(a() As String,index As Long)
    If index>=Lbound(a) And index<=Ubound(a) Then
        For x As Integer=index To Ubound(a)-1
            a(x)=a(x+1)
        Next x
        Redim Preserve a(Lbound(a) To Ubound(a)-1)
    End If
End Sub

Sub addnames(filename As String,number As Long)
    Dim As String s
    For n As Long=1 To number
        Line Input "("+Str(n)+" of "+Str(number)+")"+"  enter name to be added ";s
        ap_pend(filename,s+Chr(13,10))
    Next
End Sub

Sub removename(filename As String,remove As String)
    Redim As String names()
    loadarray(filename,names())
    dim as long n=Lbound(names)
    for n as long=Lbound(names) to ubound(names)
        If names(n)= remove Then arraydelete(names(),n):exit for
    next n
 
    Dim As String s
    For n As Long=Lbound(names) To Ubound(names)
        s+=names(n)+Chr(13,10) 're build the file
    Next
    savefile(filename,s)
End Sub

Sub show(names() As String)
    For n As Long=Lbound(names) To Ubound(names)
        Print n,names(n)
    Next
End Sub

function getrandomname(filename as string) as string
    #define range(f,l) Int(Rnd*(((l)+1)-(f))+(f))
    Redim As String names()
    loadarray(filename,names())
    return names( range(lbound(names),ubound(names)) )
end function
   


Dim As String filename="names.txt"


If Fileexists(filename)=0 Then savefile(filename,"")  'create a new file if necessary

addnames(filename,5)    'add 5 names


Redim As String names()

loadarray(filename,names())
show(names())
Print

'test removal
Dim As String g
Input "Remove a string?";g
removename(filename,g)

loadarray(filename,names())
show(names())
print
print "Get a random entry"
print getrandomname(filename)
print
print "Press a key to end . . ."
Sleep


'Kill filename  'delete all when done


 
badidea
Posts: 1365
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: Best Implementation for Set of Elements that I manually change

Postby badidea » Apr 28, 2019 22:12

I would go with:
* At start of application: Load names form file into memory
* Running application: All additions and removals in memory only
* On application exit (or an explicit save action): Overwrite the file with the names.

Consider copying the file before overwriting (1 or multiple backups).
Consider keeping the names in file (and in memory) sorted.
Consider using a linked list (not essential).
MrSwiss
Posts: 3083
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Best Implementation for Set of Elements that I manually change

Postby MrSwiss » Apr 28, 2019 22:22

@dodicat,

since I consider your savefile() to be unsafe (especially, in beginner's hands), I'd
wrap it, in order to prevent unwanted "overwriting" of a existing file.
A second grief with it is: close without the file number, which closes everything
and all open handles (even e.g. serial communication e.t.c.).
(this might be the cause, for a lenghty debugging session ...)

rewritten and, Save_to_File() wrapper added, for safe use:

Code: Select all

#Include "file.bi"

Declare Function Save_to_File(ByRef As Const String, ByRef As Const String) As Boolean
Declare Function SaveFile(ByRef As Const String, ByRef As Const String) As Boolean

Function Save_to_File( _                ' safety wrapper for: SaveFile()
    ByRef file  As Const String, _      ' [path +] filename
    ByRef sdata As Const String _       ' data to store in file
    ) As Boolean                        ' FALSE = success | TRUE = ERROR
    Var retb = FALSE                    ' no ERROR

    If FileExists(file) Then            ' check: if file is already existing
        ' ask user: 'how to proceed' ... to be implemented ...
        Print "File exists already, aborting!"  ' currently: just inform & quit
        retb = TRUE                     ' set ERROR
    Else
        retb = SaveFile(file, sdata)    ' save it (no danger of overwriting)
    End If

    Return retb
End Function

' ATTENTION: direct call can overwrite existing file !!! to avoid use Save_to_File()
Function SaveFile( _                    ' write file unconditionally (see above)
    ByRef file  As Const String, _      ' [path +] filename
    ByRef cntnt As Const String _       ' data to store in file
    ) As Boolean                        ' FALSE = success | TRUE = ERROR
    Var n = FreeFile, retb = FALSE

    If Open (file, For Binary Access Write, As #n) = 0 Then
        Put #n,, cntnt                  ' write file
        Close(n)                        ' close just the open file (not everything _
    Else                                ' else, that may be open (e.g. serial comm's)
        Print "Unable to save: " + file
        retb = TRUE
    End If

    Return retb
End Function
dodicat
Posts: 5771
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Best Implementation for Set of Elements that I manually change

Postby dodicat » Apr 29, 2019 0:17

Yes, thanks Mr Swiss, should have been close #n, a typo.
JohnM
Posts: 6
Joined: Apr 17, 2019 13:29

Re: Best Implementation for Set of Elements that I manually change

Postby JohnM » Apr 29, 2019 7:16

Thanks all. Didn't expect this elaborate of a response. I'll take it to mean that pursuing (Dim Dynamic Array => Input from File) is the most efficient way of dealing with a changing quantity of elements, and look over your code while I'm building mine, should I get stuck.

First I'll have to play around with these dynamic arrays. Thanks again.
Tourist Trap
Posts: 2756
Joined: Jun 02, 2015 16:24

Re: Best Implementation for Set of Elements that I manually change

Postby Tourist Trap » Apr 29, 2019 20:54

JohnM wrote:First I'll have to play around with these dynamic arrays. Thanks again.

As far as I remember from my own tries, it's rediming often the array that is slow (changing the size). Redim Preserve is even worst.
But if you don't redim all the time, arrays are not slow. And they are very instinctive to handle.
badidea
Posts: 1365
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: Best Implementation for Set of Elements that I manually change

Postby badidea » Apr 29, 2019 21:37

Tourist Trap wrote:As far as I remember from my own tries, it's rediming often the array that is slow (changing the size). Redim Preserve is even worst.
But if you don't redim all the time, arrays are not slow. And they are very instinctive to handle.

But still a thousand times faster (if not a million) then the time it takes a human to enter a new name :-)

A simple (incomplete) class (type) for a list of names:

Code: Select all

'a simple class (type) for a list of strings
type string_list
   private:
   dim as string entry(any)
   dim as integer num = 0
   public:
   declare sub add(value as string)
   declare sub blank(value as string)
   declare sub showAll()
   declare sub removeAll() 'to do
   declare function load(fileName as string) as integer 'to do
   declare function save(fileName as string) as integer 'to do
end type

'increase list size, add new name
sub string_list.add(value as string)
   redim preserve entry(num)
   entry(num) = value
   num += 1
end sub

'instead of removing name, blank it
sub string_list.blank(value as string)
   for i as integer = 0 to num-1
      if entry(i) = value then entry(i) = ""
   next
end sub

'print index and name
sub string_list.showAll()
   for i as integer = 0 to num-1
      print i, entry(i)
   next
end sub

dim as string_list nameList

nameList.add("Franz Kafka")
nameList.add("Ray Bradbury")
nameList.add("Virginia Woolf")
nameList.add("Aldous Huxley")

nameList.blank("Ray Bradbury")

nameList.showAll()

Blanked names could be skipped when writing to file, so they are gone on next run.
Variable "num" can be replaced with ubound statement, but easier to follow this way I think.
Note: There are many ways to to this list, this is just one. A class (type) is also not necessary.
MrSwiss
Posts: 3083
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Best Implementation for Set of Elements that I manually change

Postby MrSwiss » Apr 29, 2019 22:29

badidea wrote:But still a thousand times faster (if not a million) then the time it takes a human to enter a new name :-)

I agree.

@TT,
You like it far to much, to go a bit off-topic, just to add your 2 cents. Just don't do it, again!
(there is no need to redim anything, if you'd have read the initial post, carefully)
JohnM
Posts: 6
Joined: Apr 17, 2019 13:29

Re: Best Implementation for Set of Elements that I manually change

Postby JohnM » May 01, 2019 4:08

Having found my way to the Array section in the manual, I see I can initialize with the '...' parameter, which makes designing a counting-from-file routine somewhat unnecessary. Since I will not be manipulating the list during runtime, even utilizing a file for this purpose seems not right. The idea of using a text file was so that I could read it well myself while adding elements.

Now I think the best way would be to have a separate BI with an ellipses-declaration + initialization list. It'd be nice if I could

Code: Select all

DIM As String names(1 to ...){
"William",
"Henry",
"Richard",
"John"
}


just for ease of editing and readability, but I suppose it can't be formatted that way.
Xusinboy Bekchanov
Posts: 72
Joined: Jul 26, 2018 18:28

Re: Best Implementation for Set of Elements that I manually change

Postby Xusinboy Bekchanov » May 01, 2019 4:37

JohnM wrote:

Code: Select all

DIM As String names(1 to ...){
"William",
"Henry",
"Richard",
"John"
}


just for ease of editing and readability, but I suppose it can't be formatted that way.

It's possible:

Code: Select all

DIM As String names(1 to ...) = { _
"William", _
"Henry", _
"Richard", _
"John" _
}
JohnM
Posts: 6
Joined: Apr 17, 2019 13:29

Re: Best Implementation for Set of Elements that I manually change

Postby JohnM » May 01, 2019 5:36

Very nice. I would not have seen that in the manual. Appreciate the help on such minutiae.
fxm
Posts: 8971
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Best Implementation for Set of Elements that I manually change

Postby fxm » May 01, 2019 6:24

MrSwiss
Posts: 3083
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Best Implementation for Set of Elements that I manually change

Postby MrSwiss » May 01, 2019 12:35

While indexing (1 based) might be simpler to deal with, from a human perspective,
it is important to remember, that by default: all indexing operations are:
BASE 0
in FB, as is Standard in other languages, e.g. in C.

Therefore, my advice would be, to get used to BASE 0, which avoids later confusion
when you might have different BASE's, with different arrays.
(Example: String indexing is BASE 0, which can't be changed, as in array's!)

Run this snippet, to check it out:

Code: Select all

Dim As Long     lb1, ub1, lb2, ub2      ' bounds variables

Dim As String   ta2(...) = { "first", "second", "third", "fourth", "fifth", _
                             "sixth", "seventh", "eigth", "ninth", "tenth", _
                             "eleventh", "twelfth", "thirteenth", "fourteenth", _
                             "fifteenth", "sixteenth", "seventennth" }, _
                             ta1(Any)   ' dynamic, not yet init

' testing current array's state, first: get current bounds
lb1 = LBound(ta1) : ub1 = UBound(ta1)
lb2 = LBound(ta2) : ub2 = UBound(ta2)

Print "ta1() = dynamic (uninitialized)"
Print "array bounds are: ta1(" + Str(lb1) + " To " + Str(ub1) + ")"
Print
Print "ta2(...) = elipsis used, initialized"
Print "array bounds are: ta2(" + Str(lb2) + " To " + Str(ub2) + ")"
Print
Print "NOTE: default indexing starts with: 0 (BASE 0)"
sleep

Return to “Beginners”

Who is online

Users browsing this forum: No registered users and 1 guest