Using Array.Count and match cases F# - arrays

I am not sure yet what the problem is, I am trying to go through a ResizeArray and matching the item with the data type, and depending on this, take away the value in a specific field (iSpace) from thespace(which is how much space the inventory has), before returning the final value.
A snippet of my code :
let spaceleft =
let mutable count = 0 //used to store the index to get item from array
let mutable thespace = 60 //the space left in the inventory
printf "Count: %i \n" inventory.Count //creates an error
while count < inventory.Count do
let item = inventory.[count]
match item with
|Weapon weapon ->
thespace <- (thespace - weapon.iSpace)
|Bomb bomb ->
thespace <-(thespace - bomb.iSpace)
|Potion pot ->
thespace <- (thespace - pot.iSpace)
|Armour arm ->
thespace <- (thespace - arm.iSpace)
count <- count+1
thespace
I get an error about Int32, that has to do with the
printf "Count: %i \n" inventory.Count
line
Another problem is that thespace doesn't seem to change, and always returns as 60, although I have checked and inventory is not empty, it always has at least two items, 1 weapon and 1 armour, so thespace should atleast decrease yet it never does.
Other snippets that may help:
let inventory = ResizeArray[]
let initialise =
let mutable listr = roominit
let mutable curroom = 3
let mutable dead = false
inventory.Add(Weapon weap1)
inventory.Add(Armour a1)
let spacetogo = spaceleft //returns 60, although it should not
Also, apart from the iniitialise function, other functions seem not to be able to add items to the inventory properly, eg:
let ok, input = Int32.TryParse(Console.ReadLine())
match ok with
|false ->
printf "The weapon was left here \n"
complete <- false
|true ->
if input = 1 && spaceleft>= a.iSpace then
inventory.Add(Weapon a)
printf "\n %s added to the inventory \n" a.name
complete <- true
else
printf "\n The weapon was left here \n"
complete <- false
complete

You have spaceLeft as a constant value. To make it a function you need to add unit () as a parameter. Here's that change including a modification to make it much simpler (I've included my dummy types):
type X = { iSpace : int }
type Item = Weapon of X | Bomb of X | Potion of X | Armour of X
let inventory = ResizeArray [ Weapon {iSpace = 2}; Bomb {iSpace = 3} ]
let spaceleft () =
let mutable thespace = 60 //the space left in the inventory
printf "Count: %i \n" inventory.Count
for item in inventory do
let itemSpace =
match item with
| Weapon w -> w.iSpace
| Bomb b -> b.iSpace
| Potion p -> p.iSpace
| Armour a -> a.iSpace
thespace <- thespace - itemSpace
thespace
spaceleft () // 55
The above code is quite imperative. If you want to make it more functional (and simpler still) you can use Seq.sumBy:
let spaceleft_functional () =
printf "Count: %i \n" inventory.Count
let spaceUsed =
inventory
|> Seq.sumBy (function
| Weapon w -> w.iSpace
| Bomb b -> b.iSpace
| Potion p -> p.iSpace
| Armour a -> a.iSpace)
60 - spaceUsed

Just adding to the accepted answer: you can also match against record labels, as long as your inner types are records. Combine with an intrinsic type extension on the outer DU:
type X = { iSpace : int }
type Y = { iSpace : int }
type Item = Weapon of X | Bomb of Y | Potion of X | Armour of X
let inventory = ResizeArray [ Weapon {iSpace = 2}; Bomb {iSpace = 3} ]
let itemSpace = function
| Weapon { iSpace = s } | Bomb { iSpace = s }
| Potion { iSpace = s } | Armour { iSpace = s } -> s
type Item with static member (+) (a, b) = a + itemSpace b
60 - (Seq.fold (+) 0 inventory)
// val it : int = 55
Otherwise, you could resort to member constraint invocation expressions.
let inline space (x : ^t) = (^t : (member iSpace : int) (x))

Related

Implementation of Array data type in OCaml

I have very little knowledge about OCaml as a whole and just received an assignment to take one of the source files in the project and allow it to take a new data type (Array). I am not asking for someone to solve this problem for me, but instead I would appreciate someone walking me through this code. I would also appreciate any input on how difficult it is going to be to implement this new data type.
The file itself lacks a lot of documentation which doesn't make it any easier either.
(* * Types (hashconsed) *)
(* ** Imports *)
open Abbrevs
open Util
(* ** Identifiers *)
module Lenvar : (module type of Id) = Id
module Tysym : (module type of Id) = Id
module Groupvar : (module type of Id) = Id
module Permvar : (module type of Id) = Id
(* ** Types and type nodes *)
type ty = {
ty_node : ty_node;
ty_tag : int
}
and ty_node =
| BS of Lenvar.id
| Bool
| G of Groupvar.id
| TySym of Tysym.id
| Fq
| Prod of ty list
| Int
(* ** Equality, hashing, and hash consing *)
let equal_ty : ty -> ty -> bool = (==)
let hash_ty t = t.ty_tag
let compare_ty t1 t2 = t1.ty_tag - t2.ty_tag
module Hsty = Hashcons.Make (struct
type t = ty
let equal t1 t2 =
match t1.ty_node, t2.ty_node with
| BS lv1, BS lv2 -> Lenvar.equal lv1 lv2
| Bool, Bool -> true
| G gv1, G gv2 -> Groupvar.equal gv1 gv2
| TySym ts1, TySym ts2 -> Tysym.equal ts1 ts2
| Fq, Fq -> true
| Prod ts1, Prod ts2 -> list_eq_for_all2 equal_ty ts1 ts2
| _ -> false
let hash t =
match t.ty_node with
| BS lv -> hcomb 1 (Lenvar.hash lv)
| Bool -> 2
| G gv -> hcomb 3 (Groupvar.hash gv)
| TySym gv -> hcomb 4 (Tysym.hash gv)
| Fq -> 5
| Prod ts -> hcomb_l hash_ty 6 ts
| Int -> 7
let tag n t = { t with ty_tag = n }
end)
(** Create [Map], [Set], and [Hashtbl] modules for types. *)
module Ty = StructMake (struct
type t = ty
let tag = hash_ty
end)
module Mty = Ty.M
module Sty = Ty.S
module Hty = Ty.H
(* ** Constructor functions *)
let mk_ty n = Hsty.hashcons {
ty_node = n;
ty_tag = (-1)
}
let mk_BS lv = mk_ty (BS lv)
let mk_G gv = mk_ty (G gv)
let mk_TySym ts = mk_ty (TySym ts)
let mk_Fq = mk_ty Fq
let mk_Bool = mk_ty Bool
let mk_Int = mk_ty Int
let mk_Prod tys =
match tys with
| [t] -> t
| _ -> mk_ty (Prod tys)
(* ** Indicator and destructor functions *)
let is_G ty = match ty.ty_node with
| G _ -> true
| _ -> false
let is_Fq ty = match ty.ty_node with
| Fq -> true
| _ -> false
let is_Prod ty = match ty.ty_node with
| Prod _ -> true
| _ -> false
let destr_G_exn ty =
match ty.ty_node with
| G gv -> gv
| _ -> raise Not_found
let destr_BS_exn ty =
match ty.ty_node with
| BS lv -> lv
| _ -> raise Not_found
let destr_Prod_exn ty =
match ty.ty_node with
| Prod ts -> ts
| _ -> raise Not_found
let destr_Prod ty =
match ty.ty_node with
| Prod ts -> Some ts
| _ -> None
(* ** Pretty printing *)
let pp_group fmt gv =
if Groupvar.name gv = ""
then F.fprintf fmt "G"
else F.fprintf fmt "G_%s" (Groupvar.name gv)
let rec pp_ty fmt ty =
match ty.ty_node with
| BS lv -> F.fprintf fmt "BS_%s" (Lenvar.name lv)
| Bool -> F.fprintf fmt "Bool"
| Fq -> F.fprintf fmt "Fq"
| TySym ts -> F.fprintf fmt "%s" (Tysym.name ts)
| Prod ts -> F.fprintf fmt "(%a)" (pp_list " * " pp_ty) ts
| Int -> F.fprintf fmt "Int"
| G gv ->
if Groupvar.name gv = ""
then F.fprintf fmt "G"
else F.fprintf fmt "G_%s" (Groupvar.name gv)
It's hard to walk through this code because quite a bit is missing (definitions of Id, Hashcons, StructMake). But in general this code manipulates data structures that represent types.
You can read about hash consing here: https://en.wikipedia.org/wiki/Hash_consing (which is what I just did myself). In essence it is a way of maintaining a canonical representation for a set of structures (in this case, structures representing types) so that two structures that are equal (having constituents that are equal) are represented by the identical value. This allows constant-time comparison for equality. When you do this with strings, it's called "interning" (a technique from Lisp I've used many times).
To add arrays, you need to know whether the array type will include the length of the array or just the type of its elements. The semi-mysterious type BS seems to include a length, which suggests you may want to include the length in your reprsentation.
If I were doing this project I would look for every occurence of Prod (which represents tuples) and I'd add a type representing Array in an analogous way. Instead of a list of constituent types (as for a tuple) you have one constituent type and (I would guess) a variable representing the length of the array.
Before starting out I'd look for some documentation, on what BS represents for one thing. I also have no idea what "groups" are, but maybe you could worry about it later.
Update
Here's what I mean by copying Prod. Keep in mind that I am basing this almost entirely on guesswork. So, many details (or even the entire idea) could be wrong.
The current definition of a type looks like this:
and ty_node =
| BS of Lenvar.id
| Bool
| G of Groupvar.id
| TySym of Tysym.id
| Fq
| Prod of ty list
| Int
If you add a representation for Array after Prod you get something like this:
and ty_node =
| BS of Lenvar.id
| Bool
| G of Groupvar.id
| TySym of Tysym.id
| Fq
| Prod of ty list
| Array of Lenvar.id * ty
| Int
You would then go through the rest of the code adding support for the new Array variant. The compiler will help you find many of the places that need fixing.

F# scan a buffer finding the last part that begins \c\f and not followed by comma

Trying to find an elegant F# solution for this. I'm reading 1000 bytes from a file into a buffer, "buff". That part is easy.
Now, I want to scan the buffer looking for the last occurrence of a two-character combination:
Either a carriage return ('\r') or a line feed ('\f') that is not followed by a comma.
When I've found that, I need to find the next CR or LF (or the end of the buffer) and print the contents in between as a string.
Context: The file is a CSV file and I want the last line that has some non-empty value in the first column.
First of all, if you are reading CSV files, then it might be better idea to use CSV type provider. This gives you a nice typed access to CSV files and it has a couple of options that you can use for dealing with messy CSV files (e.g. if you need to skip a few lines). Alternatively, the F# Data library also has CSV parser, which lets you read the file using untyped API.
That said, if you really want to implement parsing on your own, then the following example should illustrate the idiomatic approach. I'm not sure I understand your problem exactly, but say we have:
let input = "start \r body \r, comma"
let buff = input.ToCharArray()
I believe you want to find the region between \r and \r,. You can do this using a recursive function that remembers the end of the range and the start of the range and decrements the starting range as it iterates over the string. You can use pattern matching to detect the cases that you need:
let rec findRange startLoc endLoc =
if startLoc < 0 then failwith "reached beginning"
match buff.[startLoc], buff.[startLoc+1] with
| ('\r' | '\f'), ',' -> findRange (startLoc - 1) startLoc
| ('\r' | '\f'), _ -> startLoc, endLoc
| _, _ -> findRange (startLoc - 1) endLoc
Using this, we can now get the range and get the required substring:
let s, e = findRange (buff.Length-2) (buff.Length-1)
input.Substring(s + 1, e - s - 1)
Elegant is in the eye of the beholder but one approach is implementing a matcher type. A matcher is function that given an input string and a position either succeeds returning a new matcher state with an updated position or fails.
// A matcher state holds a string and the position
[<Struct>]
type MatcherState =
{
Input : string
Pos : int
}
static member New i p : MatcherState = { Input = i ; Pos = p }
member x.Reposition p : MatcherState = { Input = x.Input ; Pos = p }
member x.AdvanceBy i : MatcherState = { Input = x.Input ; Pos = x.Pos + i }
member x.Current = x.Input.[x.Pos]
member x.InRange = x.Pos >= 0 && x.Pos < x.Input.Length
member x.Eos = x.Pos >= x.Input.Length
// A Matcher is a function that given a MatcherState
// returns Some MatcherState with a new position if successful
// otherwise returns None
type Matcher = MatcherState -> MatcherState option
By defining a few active patterns we can pattern match for the line start:
// Matches a line start
let mlineStart =
fun ms ->
match ms with
// Bad cases, new line followed by WS + Comma
| Cr (Ln (Ws (Comma _ | Eos _)))
| Ln (Ws (Comma _ | Eos _)) -> mbad
// Good cases, new line not followed by WS + Comma
| Cr (Ln (Ws ms))
| Ln (Ws ms) -> mgood ms
// All other cases bad
| _ -> mbad
Note: I handle new line followed by whitespace + comma here.
The line end is matched similar:
// Matches a line end
let mlineEnd =
fun ms ->
match ms with
// Good cases, new line or EOS
| Cr (Ln ms)
| Ln ms
| Eos ms -> mgood ms
// All other cases bad
| _ -> mbad
Finally we scanBackward looking for the line start and if we find it scanForward from that position until we find the line end.
match scanBackward testCase testCase.Length mlineStart with
| None -> printfn "No matching line start found"
| Some startPos ->
// Scan forwards from line start until we find a line end
match scanForward testCase startPos mlineEnd with
| None -> printfn "Line start found #%d, but no matching line end found" startPos
| Some endPos ->
let line = testCase.Substring (startPos, endPos - startPos)
printfn "Line found: %s" line
Matcher is actually a simplistic parser but that produces no values and that support scanning forward and backwards. The approach I have chosen is not the most efficient. If efficiency is important it can be improved by applying parser combinator techniques used by for example FParsec.
Hope this was interesting. I am sure someone can up with a shorter regex solution but what fun is that?
Full example follows (no quality guarantees given, use it as an inspiration)
// A matcher state holds a string and the position
[<Struct>]
type MatcherState =
{
Input : string
Pos : int
}
static member New i p : MatcherState = { Input = i ; Pos = p }
member x.Reposition p : MatcherState = { Input = x.Input ; Pos = p }
member x.AdvanceBy i : MatcherState = { Input = x.Input ; Pos = x.Pos + i }
member x.Current = x.Input.[x.Pos]
member x.InRange = x.Pos >= 0 && x.Pos < x.Input.Length
member x.Eos = x.Pos >= x.Input.Length
// A Matcher is a function that given a MatcherState
// returns Some MatcherState with a new position if successful
// otherwise returns None
type Matcher = MatcherState -> MatcherState option
let mgood ms = Some ms
let mbad = None
// Matches EOS
let meos : Matcher =
fun ms ->
if ms.Eos then
mgood ms
else
mbad
// Matches a specific character
let mch ch : Matcher =
fun ms ->
if not ms.InRange then
mbad
elif ms.Current = ch then
mgood <| ms.AdvanceBy 1
else mbad
// Matches zero or more whitespaces
let mws : Matcher =
fun ms ->
let rec loop pos =
if pos < ms.Input.Length then
let ch = ms.Input.[pos]
if ch = ' ' then
loop (pos + 1)
else
mgood <| ms.Reposition pos
else
mgood <| ms.Reposition pos
loop (max ms.Pos 0)
// Active patterns
let (|Eos|_|) = meos
let (|Comma|_|) = mch ','
let (|Cr|_|) = mch '\r'
let (|Ln|_|) = mch '\n'
let (|Ws|_|) = mws
// Matches a line start
let mlineStart =
fun ms ->
match ms with
// Bad cases, new line followed by WS + Comma
| Cr (Ln (Ws (Comma _ | Eos _)))
| Ln (Ws (Comma _ | Eos _)) -> mbad
// Good cases, new line not followed by WS + Comma
| Cr (Ln (Ws ms))
| Ln (Ws ms) -> mgood ms
// All other cases bad
| _ -> mbad
// Matches a line end
let mlineEnd =
fun ms ->
match ms with
// Good cases, new line or EOS
| Cr (Ln ms)
| Ln ms
| Eos ms -> mgood ms
// All other cases bad
| _ -> mbad
// Scans either backward or forward looking for a match
let scan steps input pos (m : Matcher) =
let rec loop ms =
match m ms with
| Some mms ->
if steps < 0 then
Some mms.Pos
else
Some ms.Pos
| None ->
if steps = 0 then
None
elif steps > 0 && ms.Pos >= ms.Input.Length then
None
elif steps < 0 && ms.Pos < 0 then
None
else
loop <| ms.AdvanceBy steps
loop (MatcherState.New input (min input.Length (max 0 pos)))
let scanForward = scan 1
let scanBackward = scan -1
[<EntryPoint>]
let main argv =
// Some test cases
let testCases =
[|
"""1,2,3,4
4,5,6,7"""
"""1,2,3,4
4,5,6,7
"""
"""1,2,3,4
4,5,6,7
,2,3,4
"""
"""1,2,3,4
4,5,6,7
,2,3,4
"""
|]
for testCase in testCases do
// Scan backwards from end until we find a line start
match scanBackward testCase testCase.Length mlineStart with
| None -> printfn "No matching line start found"
| Some startPos ->
// Scan forwards from line start until we find a line end
match scanForward testCase startPos mlineEnd with
| None -> printfn "Line start found #%d, but no matching line end found" startPos
| Some endPos ->
let line = testCase.Substring (startPos, endPos - startPos)
printfn "Line found: %s" line
0

multi-directional infinte sequence - ML

I want to use the datatype sequence that is defined as follows:
datatype 'a seq = Nil | Cons of 'a * (unit-> 'a seq);
exception EmptySeq;
fun head(Cons(x,_)) = x | head Nil = raise EmptySeq;
fun tail(Cons(_,xf)) = xf() | tail Nil = raise EmptySeq;
which has to option to iterate over functions backward and forward:
datatype direction = Back | Forward;
datatype 'a bseq = bNil | bCons of 'a * (direction -> 'a bseq);
and i defined those as well:
fun bHead(bCons(x,_)) = x | bHead bNil = raise EmptySeq;
fun bForward(bCons(_,xf)) = xf(Forward) | bForward bNil = raise EmptySeq;
fun bBack(bCons(_,xf)) = xf(Back) | bBack bNil = raise EmptySeq;
Now, what I'm trying to do is to create a function "create_seq" that gets an int "k" and returns an infinte sequence which can be iterated back and forth.
for example:
- create_seq 2;
val it = bCons (2,fn) : int bseq
- bForward it;
val it = bCons (3,fn) : int bseq
- bForward it;
val it = bCons (4,fn) : int bseq
- bBack it;
val it = bCons (3,fn) : int bseq
- bBack it;
val it = bCons (2,fn) : int bseq
- bBack it;
val it = bCons (1,fn) : int bseq
- bBack it;
val it = bCons (0,fn) : int bseq
- bBack it;
val it = bCons (~1,fn) : int bseq
this is what I've been trying to do and can't figure out why it doesn't work:
fun create_seq k = (k,fun check Forward = create_seq(k+1)
| check Back = create_seq(k-1));
nor this:
fun create_seq k = (k,fn x => case x of Forward => create_seq(k+1)
| Back => create_seq(k-1));
or even this:
fun create_seq k = (k,fn Forward => create_seq(k+1)
| Back => create_seq(k-1));
It seems I forgot the constructor:
fun intbseq(k:int) = bCons(k,fn Forward => intbseq(k+1)| Back => intbseq(k-1));
this should work.

OCaml How to write into file?

I'm looking for a way to write two ints to a file. There will be many pairs of two ints. Between the two numbers there should be a space (I mean ''). For example, something like this:
1 2
6 896
243 865
....
You can use something like this:
let rec print_numbers oc = function
| [] -> ()
| e::tl -> Printf.fprintf oc "%d %d\n" (fst e) (snd e); print_numbers oc tl
let () =
let nums = [(1, 2); (6, 896); (243, 865)] in
let oc = open_out "filename.txt" in
print_numbers oc nums;
close_out oc;
This assumes your data is a list of pairs.
If you use Core, you can do this:
open Core.Std
let () = Out_channel.write_all "your_file.txt" ~data:"Your text"

How to use a self made Type in F#?

I made a type, but I don't know how to use it properly and I don't found any solution on google.
type Sample =
{
TrackPosition : int
TubePosition : int
Barcode : string
}
let arraySamples = Array.create Scenario.Samples.NumberOfSamples **Sample**
BarcodeGenerieren.Samples.Sample
let mutable trackPosition = Scenario.Samples.StartTrackPositions
let mutable index = 1
for i in 1 .. Scenario.Samples.NumberOfSamples do
let randomNumber = System.Random().Next(0,9999)
if index > 24 then
trackPosition <- trackPosition + 1
index <- 1
arraySamples.[index] <- **new Sample{TrackPosition= trackPosition, TubePosition = index, Barcode = sprintf "100%s%06d" ((trackPosition + 1) - Scenario.Samples.StartTrackPositions) randomNumber}**
So my question is, what should I changed so that it works, when I will give the type of the array and when I will give the sample with data to the array?
You have created what is referred to as a record type. You can initialise it with the following syntax
{TrackPosition = 0;TubePosition = 0;Barcode = "string"}
your syntax in the last line is almost correct - it should be
arraySamples.[index] <- Sample{
TrackPosition= trackPosition;
TubePosition = index;
Barcode = sprintf "100%s%06d" ((trackPosition + 1) - Scenario.Samples.StartTrackPositions) randomNumber}
The changes are
Eliminate new
replace , with ;

Resources