Initial Reaction to F#
Upon reading that F# was an up and coming programming
language I decided to try it out. Even
more so upon discovering that the F must be for functional since I thought
maybe, as a Microsoft supported product, there would be more examples of how to
use it than for Elixir.
It hasn't gone all that well.
I discovered two downloads, one for yet another compiler
that has to run from a DOS window and a second that was supposed to be an IDE
with a GUI (Integrated Development Environment, Graphical User Interface).
I haven't been able to use the IDE GUI download (Visual
Studio Code - Ionide-fsharp). When I
try to debug any application it results in a request for the environment where
there are lots of possibilities. I
initially selected C, C#, a couple of others, and one for F# (ionide-fsharp if
I remember correctly). When I attempted
to act on an F# file and look through the possible environment selections, the
F# one isn't presented. And when I go
back to look through the panel with the list of possibilities, the F# option is
no longer presented. So I have not been
able to do anything with it.
The other download (Microsoft VS Code\Code.exe) I have been
able to use although it seems to be inconsistent in its ability to compile. For instance, F# allows a
"variable" to actually be a variable by specifying
"mutable" when it is declared.
But at times it will report an error that a portion of the object (such
as a field of a record structure) cannot be changed; that is, that it isn't
mutable. An example will be provided
later. Another, for instance, is that I
was able to move a value into an array of a structure type (again if I remember
correctly) but I couldn't extract it again.
That is, after copying the value into the array it was forever
lost. Or so, at least, it seemed.
Another thing that I found strange: I had the "bright" idea that it
might be easier to parse the lines of the Learn to Code GR file by reading one
character at a time and immediately checking if the character was one of the
ones that delimited the fields of interest.
This worked well enough. But
resulted in finding the strange part. As
I read a single character from the file, it had to be read into a buffer at the
same position that it was in the file.
That is, the implementation of the read of N bytes of the file starting
at M couldn't be to a buffer that was sized as N bytes. Instead, to read the entire file this way would
mean having a buffer at least the size of the contents of the file.
Besides these problems with the compiler, one of the reasons
for trying F# was that I thought there would be plenty of examples of its use
from Microsoft so that it would be easier to obtain some answers than for
Elixir. Yet each time I looked up a
feature the Microsoft site would have that examples for C, C++, and C# were
available but that a F# example wasn't available.
Oh well. With the
ability to use mutable I was making progress with the Learn to Code GR
application while having the problems with the compiler's idiosyncrasies. But, of course, this was unhelpful to being
able to use Elixir since all the talk on the internet was to avoid using
mutable since it wasn't a functional language feature and how learning to do
without it would allow one to find out how wonderful a functional language
could be. Needless to say I'm still
awaiting that moment and still in favor of languages that allow me to code.
Another problem was sometimes the compiler would degree a
construct like
let
valueArray = [| for i in 0 .. 19 -> "" |]
as unknown when it was referenced later and sometimes it
would recognize that it did exist. It
seemed to just depend upon which errors it was reporting.
And yet another problem would be that it would declare
perfectly valid constructs in any other language as code to be implicitly
ignored. This is, not allowing certain
code due, I suppose, having deduced that the code segment could never execute
when, in fact, if it allowed the code sequence, it would execute as conditions
varied.
By reworking the code I was able to get around problems such
as the above by using do loops instead of while loops. However, since there isn't a
"break" to exit the loop early I had to allow all the loops to continue
to the end while adding an if statement to avoid actually doing anything from
the point where one would normally exit the loop. That is, the compiler seemed to have problems with the code
inside of a while loop.
Using it reminded me of my experience with Java Script. With Java Script html files I had to wait
until running the application to find that the file had to be fixed. That is, no compiler. And with the .js files the compiler missed
various erroneous constructs so they had to be found when attempting to run the
application. So I learned that only
small changes should be made at a time; the better to be able to find where an
error had been introduced.
Likewise with this F# compiler. A change could cause the compiler to report many errors to lines
of code that hadn't been reported to be in error before. That is, the actual error could be far
removed from the ones being reported.
Therefore, like Java Script I found it best to make changes in small
increments so if errors were reported elsewhere I would know that the reported
code was not in error and that it was something in the newly added/changed code
that was causing the problem.
Although I couldn't find examples on Microsoft sites of F#
code that did provide examples for other Microsoft supported languages I did
find later on http://visualfsharp.com/ which provides information on
"Microsoft Visual F# Programming" with text and examples in 30
different sections. Although its
examples aren't real world examples they were helpful. (By real world I mean that, for instance for
a car dealership, the data would be pre-canned in the code rather than coming
from a database. That is, the inventory
along with the price, etc would be in the code whereas in the real world a
change in such data would not require a change to the program.)
I read through the entire set of online pages and copied the
contents into a Word document so that I could refer to before I returned to
coding the Learn to Code application.
Learn to Code GR
My attempt to use F# was to repeat the Learn to Code GR
application of all my recent posts.
As mentioned above, I coded it in small segments to avoid
the reporting of errors where no error existed. Likewise, so that errors could be immediately fixed before
proceeding. Note, as a for instance, an
error that occurred at the very end.
The code
. . .
let mutable
maxPair : KeyTable = nullItem
. . .
if
maxSum > maxPair.Sum then
maxPair.Key <- item.Key
maxPair.Value <- item.Value
maxPair.Sum <- maxSum
doesn't compile.
Each of the lines to set one of maxPair fields yields the error
error FS0005: This field is not mutable
even thou maxPair has been declared to be mutable. However, either of the constructs
maxPair <- { Key = item.Key
Value = item.Value
Sum = maxSum }
and
let
tempPair = {
Key = item.Key
Value = item.Value
Sum = maxSum
}
maxPair <- tempPair
compile satisfactorily.
That is, the record fields of a mutable record variable can't be
assigned individually but can be in total.
Another difference from what occurred using other languages
that I learned about was that reading a line from a text file does not result
in the trailing carriage return and line feed/new line characters being
input. Therefore, all the lines of the
file that are input are like the last line of the file. That is, without either of the trailing CR
or NL characters. Therefore, the
statements in the FindSeparator function that check for CR and NL could be
removed. This does simplify the parse
of the line since it makes all the lines have a similar format.
Another comment is that I found using an array of a record
type object much easier to use than attempting to use a Key and KeyTable class
that I used in other languages that supported a class. Therefore, the code declares a KeyTable
record type and then a keyTable array of null KeyTable objects. (This because the array has to be sized and
initialized when it is declared.)
The declaration of a function and of a variable (where
variables are either constant or variable depending upon whether declared as
mutable) both start with the let keyword and have the = sign to
denote that the body follows. The
specification of the type is usually not necessary since F# is supposed to be
able to figure it out. But I have
chosen to supply it (at least most of the time). '<-' is used to assign a new value to a mutable variable.
The use of indentation and white space is mostly used rather
than brackets, trailing ';' symbols, ',' in function calling parameters to use
as separators, and such. Therefore,
careful attention is required in regards to indentation so as to group code as
desired.
The F# version of the Learn To Code GR application follows.
module LearnToCodeGRModule =
open System
open
System.IO
open
System.Net
// Create
record type
type
KeyTable = {
Key :
string
Value
: string
Sum :
int
}
// Continue
use of record type by creating global keyTable array
let
nullItem = {
Key =
""
Value =
""
Sum = 0
}
let mutable
keyTable : KeyTable array =
[| for i
in 0..59 -> nullItem |]
// Add new
record to keyTable
let
AddRecord (start : int) (key : string) (value : string) =
let
mutable srchItem : KeyTable = nullItem
let
mutable i : int = start
let
mutable cont : string = "Yes"
for i =
start to keyTable.Length-1 do
srchItem <- keyTable.[i]
if
srchItem.Sum = 0 then
if cont
= "Yes" then
let
mutable newItem : KeyTable = {
Key = key
Value = value
Sum = 1
}
keyTable.[i] <- newItem
cont <- "No"
//end AddRecord function
// Update
keyTable with new key, value pair
let Update
(key : string) (value : string) =
let
mutable ignoreItem : string = "No"
let
mutable item : KeyTable = nullItem
for i = 0
to keyTable.Length-1 do
item
<- keyTable.[i]
if
ignoreItem = "No" then
if
item.Sum = 0 then // reached empty location without a match
AddRecord i key value
ignoreItem <- "Yes"
elif
item.Key = key then // key matches to array item
if
item.Value = value then // value also matches
let mutable modItem : KeyTable = {
Key = item.Key
Value = item.Value
Sum = item.Sum + 1
}
keyTable.[i] <- modItem
ignoreItem <- "Yes"
//end of Update function
// Find
next field of interest indicator character
let HT :
char = '\t'
let CR :
char = '\r'
let NL :
char = '\n'
let
FindSeparator(segment : string) : int =
let
mutable sepLocation = -1
for c = 0
to segment.Length-1 do
if
sepLocation = -1 then
if
segment.[c] = HT || segment.[c] = CR || segment.[c] = NL then
sepLocation <- c
if
sepLocation = -1 then // no final separator
sepLocation <- segment.Length
sepLocation
//end of FindSeparator function
// Parse
the line and Update the keyTable
let
ParseLine(line) =
let
mutable endField = -1
let
mutable partialLine : string = line
let
mutable discard : string = ""
let
mutable key : string = ""
let
mutable value1 : string = ""
let
mutable value2 : string = ""
for f = 0
to 3 do // four fields of interest expected
let
endField : int = FindSeparator(partialLine)
let
mutable seg : string = partialLine.[0..endField-1]
if f =
0 then
discard <- seg
elif f = 1 then
key
<- seg
elif f
= 2 then
value1 <- seg
else
value2 <- seg
partialLine <- partialLine.[endField+1..partialLine.Length-1]
// Update
keyTable with the key, value pairs of the line
Update
key value1
Update
key value2
//end of ParseLine function
// Read and
process lines from the file of path
let
ByLine(path) =
use input
= File.OpenText(path)
printfn
"Data read from the file follows"
while not
input.EndOfStream do
let line = input.ReadLine()
printfn
"%s" line
ParseLine( line )
//end of ByLine function
// Read the
file and perform the task. This is the
main function.
ByLine(
"C:/Source/LearnToCodeGR/max-col-sum-by-Key.tsv" )
// Report
the results
// Note:
Instead of invoking a Report function at the end of the ByLine
// function it is included here as an
extension of the main function
printfn
""
printfn
"Report of the results"
printfn
"Key Value Sum Total Max"
let mutable
maxPair : KeyTable = nullItem
let mutable
keyTotal = 0
let mutable
maxSum = keyTable.[0].Sum
let mutable
currentKey = keyTable.[0].Key
let mutable
item : KeyTable = nullItem
for i = 0
to keyTable.Length-1 do
item
<- keyTable.[i]
if item.Sum
<> 0 then // a non-null array position
if
item.Key <> currentKey then
printfn "
%d %d" keyTotal maxSum
currentKey <- item.Key // new key
keyTotal <- item.Sum //
initial Sum of new key
maxSum <- item.Sum
if
maxSum > maxPair.Sum then
maxPair <- { Key = item.Key
Value = item.Value
Sum = maxSum }
else
if
item.Sum > maxSum then
maxSum <- item.Sum
if
maxSum > maxPair.Sum then
maxPair <- { Key = item.Key
Value = item.Value
Sum = maxSum }
keyTotal <- keyTotal + item.Sum
printfn
"%s %s %d" item.Key item.Value item.Sum
printfn
" %d %d"
keyTotal maxSum // of last element
printfn
" "
printfn
"Key and Value with the most references is"
printfn
"Key Value Sum"
printfn
"%s %s %d" maxPair.Key maxPair.Value maxPair.Sum
where the results, as seen on the console or to a file piped
to capture the console output – that is,
learntocodegr > LearnToCodeGRresults.dat
are as follows.
Data read from the file follows
0,912_NUM 1000 1 1
0,912_NUM 1000 2 1
0,912_NUM 1000 1 1
0,912_NUM 1000 2 2
0,912_NUM 1000 1 1
0,912_NUM 1000 2 2
0,912_NUM 1000 1 1
0,912_NUM 1000 1 1
0,912_NUM 1000 2 2
0,912_NUM 1000 1 1
0,912_NUM 2000 1 1
0,912_NUM 2000 1 1
0,912_NUM 2000 1 1
0,912_NUM 2000 1 1
0,912_NUM 2000 2 2
0,912_NUM 2000 1 1
0,912_NUM 2000 1 1
0,912_NUM 2000 2 2
0,912_NUM 2000 1 1
0,912_NUM 2000 1 1
0,912_NUM 3000 1 1
0,912_NUM 3000 1 1
0,912_NUM 3000 1 1
0,912_NUM 3000 1 1
0,912_NUM 3000 1 1
0,912_NUM 3000 1 1
0,912_NUM 3000 1 1
0,912_NUM 3000 1 1
0,912_NUM 3000 1 1
0,912_NUM 3000 1 1
0,912_NUM 4000 1 1
0,912_NUM 4000 1 1
0,912_NUM 4000 3 3
0,912_NUM 4000 2 1
0,912_NUM 4000 1 1
0,912_NUM 4000 1 1
0,912_NUM 4000 1 1
0,912_NUM 4000 1 1
0,912_NUM 4000 4 2
0,912_NUM 4000 1 1
0,912_NUM 5000 2 2
0,912_NUM 5000 2 2
0,912_NUM 5000 1 1
0,912_NUM 5000 1 1
0,912_NUM 5000 1 1
0,912_NUM 5000 2 2
0,912_NUM 5000 3 1
0,912_NUM 5000 2 2
0,912_NUM 5000 4 3
0,912_NUM 5000 3 3
Report of the results
Key Value
Sum Total Max
1000 1 13
1000 2 7
20 13
2000 1 16
2000 2 4
20 16
3000 1 20
20 20
4000 1 15
4000 3 2
4000 2 2
4000 4 1
20 15
5000 2 8
5000 1 7
5000 3 4
5000 4 1
20 8
Key and Value with the most references is
Key Value
Sum
3000 1 20
No comments:
Post a Comment