Split huge F# file with mutually recursive functions - f#

In F# I have a very long code file like
let rec f1 a1 a2 a3 a4 a5 .. aN =
...
and f2 a1 a2 a3 a4 a5 ... aN =
...
and f3 a1 a2 a3 a4 a5 ... aN =
...
and f40 a1 a2 a3 a4 a5 ... aN =
...
In other words there are many mutually recursive functions, each with a lot of parameters.
Now the problem is that the file is 17000 lines long, and Visual Studio has become too slow. (For example, I can't hover the mouse over an item to see its type; if I press the dot, there is no completion, and so on)
Therefore I need to split the file into many smaller files. But I can't see a mechanical and easy way to do it.
Could you please give me an advice? I would like a mechanical way to split the code into multiple files, which does not involve writing the type of all functions (preserve type inference).

In the meantime I found a solution (tested):
This is the initial situation (simplified to only have four functions, but in reality they are many more):
let rec f1 a b c =
f2 a b c;
f3 a b c;
f4 a b c;
and f2 a b c =
f1 a b c;
f3 a b c
f4 a b c
and f3 a b c =
f1 a b c;
f2 a b c
f4 a b c
and f4 a b c =
f1 a b c;
f2 a b c
f3 a b c
And here is the solution:
Suppose you decide to move f3 to another file. Then you can split the file above in two files as follows:
FILE 1
======
let callRef mf =
match !mf with
| None -> failwith "function ref is none"
| Some f -> f
let r_f3 = ref None;
let rec f1 a1 a2 a3 =
f2 a b c;
callRef r_f3 a1 b1 c1;
f4 a1 b1 c1;
and f2 a1 a2 a3 =
f1 a b c;
callRef r_f3 a1 b1 c1;
f4 a1 b1 c1;
and f4 a1 a2 a3 =
f1 a b c;
f2 a1 b1 c1;
callRef r_f3 a1 b1 c1;
FILE 2
======
let f3 a1 a2 a3 =
f1 a b c;
f2 a1 b1 c1;
f4 an bn cn;
Then, in the main initialization function (which is in a third file), you need to do
r_f3 := Some f3;
And that's it.
Repeat the same strategy to move f1, f2 and f4 out of the first file.
Update: This solution works well for functions which return unit, but unfortunately for functions which return an actual type it forces you to specify the function type explicitely, e.g.
let (r_f3 : (t1 -> t2 -> t3 -> t4 -> t5) option ref) = ref None;
or you can do this:
let (r_f3 : 'a option ref) = ref None;
but you'll get a compiler warning.

Related

how to run formula for a range (like a loop) and sum the results?

In google sheets, I am using a formula like this:
=SUM(
VLOOKUP(D2,I2:J8,2,FALSE) * E2,
VLOOKUP(D3,I2:J8,2,FALSE) * E3,
VLOOKUP(D4,I2:J8,2,FALSE) * E4,
VLOOKUP(D5,I2:J8,2,FALSE) * E5,
VLOOKUP(D6,I2:J8,2,FALSE) * E6
)
How to write it as a range? like
=SUM(VLOOKUP(x,I2:J8,2,FALSE) * y)
x is D2 to D6
y is E2 to E6
Try this:
=arrayformula(SUM(VLOOKUP(D2:D6,I2:J8,2,FALSE)*E2:E6))

Google Sheets QUERY with dropdown menu + multiple conditions

I have this kind of table on a tab (called "Log"):
A B C D E F G H
a1 b1 c1 d1 5 f1 g1 h1
a2 b1 c2 d1 3 f2 g2
a3 b2 c1 d2 4 f3 g3 h2
a4 b1 d1 5 f4 g4
a5 b2 c3 d1 3 f5 g5 h3
On another tab (called "Watch") of the same file I have a dropdwon menu with all the "D"s.
I'm trying, on the "Watch" tab, with the QUERY function to visualize C,E,G and H. C,E and H have to always be visualized while I need G only if "E"s are between "1" and "3".
The closest I got was this:
=QUERY(Log!B:H, "SELECT C,E,H,G WHERE D='"&B1&"' and H is not null and E<=3")
but it shows only where "E"s are <=3 ignoring the choice from the dropdown menu (WHERE D='"&B1&"')
try:
=QUERY(Log!B:H,
"select C,E,H,G
where lower(D) = '"&TRIM(LOWER(B1))&"'
and H is not null
and E<=3", 0)
Try with this:
=filter({Log!C1:C5,Log!E1:E5,Log!H1:H5,arrayformula(if((Log!H1:H5="")*(Log!E1:E5<=3),Log!G1:G5,""))}, Log!D1:D5="d1")
or
=filter({Log!C1:C5,Log!E1:E5,Log!H1:H5,arrayformula(if((Log!H1:H5="")*(Log!E1:E5<=3),Log!G1:G5,""))}, Log!D1:D5 = B1)
or
=filter({Log!C1:C5,Log!E1:E5, Log!H1:H5,if((Log!H1:H5="")*(Log!E1:E5<=3)=1,Log!G1:G5,"")},Log!D1:D5="d1")
The Result:
c1 5 h1
c2 3 g2
5
c3 3 h3

Cartesian product in Z3

I have two different sets for example {e1, e2, e3} type of E1 and {g1, g2, g3} type of E2 which defined as follows:
E1 = DeclareSort('E1')
e1,e2,e3 = Consts('e1 e2 e3', E1)
E2 = DeclareSort('E2')
g1,g2,g3 = Consts('g1 g2 g3', E2)
My question is how can I make Z3 give me a Cartesian product between the two sets. I need the result like following:
{(e1, g1),(e1, g2),(e1, g3),(e2, g1),(e2, g2),(e2, g3),(e3, g1),(e3, g2),(e3, g3)}.
Anyone can help?
Thanks
Note that e1,e2, e3, g1, g2, g3 are variables. They are not different by definition. So, the assertion e1 == e2 is satisfiable.
You can create products in Python in the usual way:
[(x,y) for x in [e1,e2,e3] for y in [g1,g2,g3]]
If you want to create the product of two sorts, you can use algebraic data-types.
Declare a datatype "Tuple"
>>> Tuple = Datatype('Tuple')
>>> Tuple.declare('mk_tuple', (('first', E1), ('second', E2)))
>>> Tuple = CreateDatatypes(Tuple)[0]
>>> Tuple.mk_tuple(e1, g1)
mk_tuple(e1, g1)
Maybe you're looking for itertools.product()?
>>> import itertools
>>> for x in itertools.product(['e1', 'e2', 'e3'], ['g1', 'g2', 'g3']):
... print(x)
...
('e1', 'g1')
('e1', 'g2')
('e1', 'g3')
('e2', 'g1')
('e2', 'g2')
('e2', 'g3')
('e3', 'g1')
('e3', 'g2')
('e3', 'g3')
>>>

Array.map2 type inference on records

I ran into something with Array.map2 that I don't understand.
Consider this code:
type r1 = {
v1 : int
X : int
}
type r2 = {
v1 : int
Y : int
}
let a1 = [|{v1=1; X=1}; {v1=2; X=2}|] // val a1 : r1 [] ...
let a2 = [|{v1=100; Y=100}; {v1=200; Y=200}|] // val a2 : r2 [] ...
Array.map2 (fun x1 x2 -> (x1.X, x2.Y)) a1 a2 // works as expected
Array.map2 (fun x1 x2 -> (x1.v1, x2.v1)) a1 a2 // error FS0001: Type mismatch. Expecting a r2 [] but given a r1 []
Both records have a field v1. In the last line, I try to get a tuple of the values of v1, but from the different record types r1 and r2. It seems to throw an error when I try to select fields that have the same name on different records.
Somehow it jumps to the conclusion that x1 must be of type r2, why doesn't the second line infer the type from the two parameters a1 and a2?
edit
This fixes it but I would expect to get the type right from the parameters.
Array.map2 (fun (x1:r1) x2 -> (x1.v1, x2.v1)) a1 a2 // works
The type checker works from left to right, so there isn't enough information to resolve conflicts in the second example. Your first example is fine because .X and .Y are unique fields on r1 and r2 respectively.
That said, in this case you could use piping to make types of a1 and a2 available to the type checker prior accessing record fields:
(a1, a2) ||> Array.map2 (fun x1 x2 -> (x1.v1, x2.v1))
In general, you should use record patterns which contain unique fields to identify correct types:
Array.map2 (fun {v1 = a; X = _} {v1 = b; Y = _} -> (a, b)) a1 a2
or provide fully qualified field names:
Array.map2 (fun {r1.v1 = a} {r2.v1 = b} -> (a, b)) a1 a2

Parse Tab Delimited String

I'm having some trouble figuring out how to separate a string which is tab delimited into chunks of data as an example if i have a text file which I'm reading from that looks like this
a1 b1 c1 d1 e1
a2 b2 c2 d2 e2
and i read the first line of my file and get a string which of
"a1 b1 c1 d1 e2"
I want to separate this into 5 variables a,b,c,d and e, or create a list (a b c d e). Any thoughts?
Thanks.
Try concatenating parentheses onto the front and back of your input string, then using read-from-string (I assume you're using Common Lisp, since you tagged your question clisp).
(setf str "a1 b1 c1 d1 e2")
(print (read-from-string (concatenate 'string "(" str ")")))
Yet another way to go about it (a tad more robust, perhaps), You can also easily modify it so that you could `setf' a character in the string once the callback is called, but I didn't do it that way because it seemed like you don't need this sort of ability. Also, in that later case, I'd rather use a macro.
(defun mapc-words (function vector
&aux (whites '(#\Space #\Tab #\Newline #\Rubout)))
"Iterates over string `vector' and calls the `function'
with the non-white characters collected so far.
The white characters are, by default: #\Space, #\Tab
#\Newline and #\Rubout.
`mapc-words' will short-circuit when `function' returns false."
(do ((i 0 (1+ i))
(start 0)
(len 0))
((= i (1+ (length vector))))
(if (or (= i (length vector)) (find (aref vector i) whites))
(if (> len 0)
(if (not (funcall function (subseq vector start i)))
(return-from map-words)
(setf len 0 start (1+ i)))
(incf start))
(incf len))) vector)
(mapc-words
#'(lambda (word)
(not
(format t "word collected: ~s~&" word)))
"a1 b1 c1 d1 e1
a2 b2 c2 d2 e2")
;; word collected: "a1"
;; word collected: "b1"
;; word collected: "c1"
;; word collected: "d1"
;; word collected: "e1"
;; word collected: "a2"
;; word collected: "b2"
;; word collected: "c2"
;; word collected: "d2"
;; word collected: "e2"
Here's an example macro you could use, if you wanted to modify the string as you read it, but I'm not entirely happy with it, so maybe someone will come up with a better variant.
(defmacro with-words-in-string
((word start end
&aux (whites '(#\Space #\Tab #\Newline #\Rubout)))
s
&body body)
`(do ((,end 0 (1+ ,end))
(,start 0)
(,word)
(len 0))
((= ,end (1+ (length ,s))))
(if (or (= ,end (length ,s)) (find (aref ,s ,end) ',whites))
(if (> len 0)
(progn
(setf ,word (subseq ,s ,start ,end))
,#body
(setf len 0 ,start (1+ ,end)))
(incf ,start))
(incf len))))
(with-words-in-string (word start end)
"a1 b1 c1 d1 e1
a2 b2 c2 d2 e2"
(format t "word: ~s, start: ~s, end: ~s~&" word start end))
assuming that they are tabbed (not spaced) then this will create a list
(defun tokenize-tabbed-line (line)
(loop
for start = 0 then (+ space 1)
for space = (position #\Tab line :start start)
for token = (subseq line start space)
collect token until (not space)))
which results in the following:
CL-USER> (tokenize-tabbed-line "a1 b1 c1 d1 e1")
("a1" "b1" "c1" "d1" "e1")

Resources