Hi I have the following code:
show_result(Squares,MaxRow,MaxCol) :-
show_result(Squares,MaxRow,MaxCol,1), nl.
show_result(_,MaxRow,_,Row) :- Row > MaxRow, !.
show_result(Squares,MaxRow,MaxCol,Row) :-
show_result(Squares,MaxRow,MaxCol,Row,1), nl,
Row1 is Row+1, show_result(Squares,MaxRow,MaxCol,Row1).
show_result(_,_,MaxCol,_,Col) :- Col > MaxCol, !.
show_result(Squares,MaxRow,MaxCol,Row,Col) :-
(memberchk(sq(Row,Col,X),Squares), !, write(X); write('#')),
Col1 is Col+1, show_result(Squares,MaxRow,MaxCol,Row,Col1).
When I run the code it shows the correct output on the screen.
But I want to write it to a file for which I have modified the code in this way:
show_result(Squares,MaxRow,MaxCol,SolutionFile) :-
show_result(Squares,MaxRow,MaxCol,1,SolutionFile),
open(SolutionFile,write,Stream),
nl(Stream), close(Stream).
show_result(_,MaxRow,_,Row,SolutionFile) :- Row > MaxRow, !.
show_result(Squares,MaxRow,MaxCol,Row,SolutionFile) :-
show_result(Squares,MaxRow,MaxCol,Row,1,SolutionFile),
open(SolutionFile,write,Stream),nl(Stream), close(Stream),
Row1 is Row+1,
show_result(Squares,MaxRow,MaxCol,Row1,SolutionFile).
show_result(_,_,MaxCol,_,Col,SolutionFile) :- Col > MaxCol, !.
show_result(Squares,MaxRow,MaxCol,Row,Col,SolutionFile) :-
( memberchk(sq(Row,Col,X),Squares),
!,
open(SolutionFile,write,Stream), write(Stream,X), close(Stream)
; open(SolutionFile,write,Stream), write(Stream,'#'), close(Stream)
),
Col1 is Col+1,
show_result(Squares,MaxRow,MaxCol,Row,Col1,SolutionFile).
This returns 'true' but doesn't write anything to the file.
What do I need to change to get the output written to the file?
Use your original program and:
..., once_to_file(show_result(Squares,MaxRow,MaxCol)), ...
once_to_file(Goal, File) :-
open(File, write, S),
with_output_to(S,once(Goal)),
close(S).
This can be further improved using setup_call_cleanup/3.
But seriously, it would be much better for you to "write" the information into a list via a dcg. In this manner you would have a clean, program for this part too.
although this seems overly inefficient, you could try to change the open/3 mode, from write to append.
Much better would be to pass around the file descriptor, instead of SolutionFile. So I would suggest
show_result(Squares,MaxRow,MaxCol,SolutionFile) :-
open(SolutionFile,write,Stream),
show_result(Squares,MaxRow,MaxCol,1,Stream),
nl(Stream), close(Stream).
and remove all other open/3 from those predicates, writing instead into SolutionFile:
how_result(_,MaxRow,_,Row,SolutionFile) :- Row > MaxRow, !.
show_result(Squares,MaxRow,MaxCol,Row,SolutionFile) :-
show_result(Squares,MaxRow,MaxCol,Row,1,SolutionFile),
nl(SolutionFile),
Row1 is Row+1,
show_result(Squares,MaxRow,MaxCol,Row1,SolutionFile).
show_result(_,_,MaxCol,_,Col,SolutionFile) :- Col > MaxCol, !.
show_result(Squares,MaxRow,MaxCol,Row,Col,SolutionFile) :-
( memberchk(sq(Row,Col,X),Squares),
!,
write(SolutionFile,X)
; write(SolutionFile,'#')
),
Col1 is Col+1,
show_result(Squares,MaxRow,MaxCol,Row,Col1,SolutionFile).
note: untested code.
Since you mention that the output is already correct, an alternative, available in SWI-Prolog, would be to keep the origincal program unchanged, and call it using with_output_to.
Another alternative, use old fashioned IO, prefixing execution with tell/1, and resume after execution with told/0. But this IO modality is deprecated, since it leads to several difficulties...
Related
I've created a simple table and trying to split data with subtotals.
A indicates the subtotal lines.
B contains the rows number for previous subtotal. This is just extra field to simplify formulas.
C Contains some amounts.
D Contains subtotals of amounts between previous and current subtotal line.
The subtotal formula has the following view:
=ArrayFormula(
IF($A2:$A; MMULT(
($B2:$B < TRANSPOSE(ROW($A2:$A))) * (TRANSPOSE(ROW($A2:$A)) < ROW($A2:$A));
IF(ISNUMBER(C2:C); C2:C; 0)
); )
)
The problem is that the formula is extrimely slow. Is there a way to make it faster?
Example file:
https://docs.google.com/spreadsheets/d/1HPGeLZfar2s6pIQMVdQ8mIPzNdw2ESqKAwZfo4IicnA/edit?usp=sharing
You could also try this much simpler formula:
=ArrayFormula(
if(B3:B="","",
sumif(row(B3:B),"<="&row(B3:B),C3:C)-
sumif(row(B3:B),"<="&B3:B,C3:C)
)
)
Yes there is
The easier is to remove the blank rows below the data range.
One that might require maintenance, replace open reference like $A2:$A by closed references, i.e. $A2:$A100
One that incresase the formula complexity an volatility, put each open reference inside ARRAY_CONSTRAIN but it's easier to maintain in case that new data rows were added
use the "necessary" range:
=ARRAYFORMULA(IFERROR(IF(A2:A; MMULT((
INDIRECT("B2:B"&MAX(IF(B2:B="";; ROW(B2:B)))) < TRANSPOSE(ROW(
INDIRECT("A2:A"&MAX(IF(A2:A=TRUE; ROW(A2:A); )))))) * (TRANSPOSE(ROW(
INDIRECT("A2:A"&MAX(IF(A2:A=TRUE; ROW(A2:A); ))))) < ROW(
INDIRECT("A2:A"&MAX(IF(A2:A=TRUE; ROW(A2:A); ))))); IF(ISNUMBER(
INDIRECT("C2:C"&MAX(IF(C2:C="";; ROW(C2:C)+1))));
INDIRECT("C2:C"&MAX(IF(C2:C="";; ROW(C2:C)+1))); 0)); )))
this should be way faster...
I'm not sure how to word this question so I'll try my best to explain it:
Lets say I have a file:
100001,ABC,400
100001,EFG,500
100001,ABC,500
100002,DEF,400
100002,EFG,300
100002,XYZ,1000
100002,ABC,700
100003,DEF,400
100003,EFG,300
I want to grab each row and group them together where the first value in each row is the same. So all 100001's go together, all 100002's go together, etc.
I just need help figuring out the logic. Don't need a specific implementation in a language.
Pseudocode is fine.
I assume the lines are in order by COL1.
I assume "go together" means they are concatenated into one line.
The logic with pseudocode:
while not EOF
read line
if not same group
if not first line
print accumulated values
start new group
append values
print the last group
In awk you can test it with the following code:
awk '
BEGIN { FS = ","; x=""; last="";}
{
if ($1 != last) {
if (x != "")
print x;
x=$1;
last=$1;
}
x=x";"$2";"$3;
}
END {print x;} '
I have a Google sheet with fixed number of columns and dynamic rows.
I like to use countA to count fields with a value (non-blank) in the current row.
I found a formula here but don't understand it, neither can get it to work.
ArrayFormula(MMULT( LEN(A1:E)>0 ; TRANSPOSE(SIGN(COLUMN(A1:E1)))))
Sheet gives me error: "Function MMULT parameter 1 expects number values. But 'TRUE' is a boolean and cannot be coerced to a number."
The formula should work if you convert the booleans (true or false) returned by LEN(A1:E)>0 to numbers (1 or 0), as Barry already mentioned. This can be done quite easily by wrapping the output of the LEN()-function in an N-function or by preceding it with '--'. So, assuming your data starts in row 2, see if this works:
=ArrayFormula(MMULT( --(LEN(A2:E)>0) , TRANSPOSE(COLUMN(A2:E2)^0)))
An alternative way would be to use COUNTIF()
=ArrayFormula(COUNTIF(IF(A2:E<>"", row(A2:A),),row(A2:A)))
and probably even a combination should work:
=ArrayFormula(MMULT( --(A2:E<>"") , TRANSPOSE(COLUMN(A2:E1)^0)))
If you also want to include a header row, try:
=ArrayFormula(if(row(A:A)=1, "Header", MMULT( --(LEN(A:E)>0) , TRANSPOSE(COLUMN(A1:E1)^0))))
or
=ArrayFormula(if(row(A:A)=1, "Header", MMULT( --(A:E<>"") , TRANSPOSE(COLUMN(A1:E1)^0))))
or
=ArrayFormula(if(row(A:A)=1, "Header", COUNTIF(IF(not(isblank(A:E)), row(A:A),),row(A:A))))
EDIT: (after new question in comments)
If you want to sum the values, you can do that with MMULT() too:
=ArrayFormula(if(row(A:A)=1, "Header", MMULT(if(A1:E<>"", A1:E,0), transpose(column(A1:E1)^0))))
or using sumif:
=ArrayFormula(if(row(A:A)=1, "Header", sumif(IF(COLUMN(A1:E1),ROW(A1:A)),ROW(A1:A),A1:E)))
NOTE: if you want to limit the output to let's say the last row that has values in col A, try:
=ArrayFormula(if(row(A:A)=1, "Header", IF(LEN(A1:A), MMULT(if(A1:E<>"", A1:E,0), transpose(column(A1:E1)^0)),)))
or, again with sumif()
=ArrayFormula(if(row(A:A)=1, "Header", if(len(A1:A), sumif(IF(COLUMN(A1:E1),ROW(A1:A)),ROW(A1:A),A1:E),)))
That formula seems a little complex for your explanation, can't you just use this formula copied down
=COUNTA(A1:E1)
...but specifically addressing your question, you need to change this part
LEN(A1:E)>0
...so that it returns numbers - try
IF(LEN(A1:E)>0;1;0)
Solr FunctionQuery has a DIV(x,y) function. I have such a need if y=0, then y should be equal to x.
In other words, I need to represent the following logic with FunctionQuery:
if y == 0, return 1 /* i.e. DIV(x,x) */
else, return DIV(x,y)
Somehow, from the Solr doc, I cannot find any comparison function, e.g. EQ(x, value), etc. for me to use.
Will anyone be able to give me a hint to construct my desired logic using FunctionQuery?
Thanks!
To clean up this question and log what is my final solution, thanks to Srikanth Venugopalan comment:
actually you need to switch the arguments. exposure_count = 0 is interpreted as false. So your condition would be {!boost b=if(exposure_count,div(1,exposure_count),1)}"
As it seems, Lucid works documentation does have a mistake. The FunctionQuery parser does not take comparison operators such as ==, at least this is what I found by looking into the sourcecode. Also, the field separator for IF() function should be ,(comma) and not ;(semi-colon).
The official Solr wiki is correct.
For string terms this works:
if(termfreq(fieldname,"value"),2,1)
which yields 2 if "value" is contained in "fieldname" (termfreq will be >0)
you can use == for equals and if conditional statement as below :-
e.g. if(y == 0; 1; DIV(x,y))
Check for the example # Documentation
if(color=="red"; 100; if(color=="green"; 50; 25)) :
This function checks the document field "color", and if it is "red" returns 100, if it is "green" returns 50, else returns 25.
Edit :-
Solr if wiki mentions , (comma) as seperator
e.g. if(exists(myField),100,0)
I got this code that has to run a database as long as N > Order.
My code only runs once :/ ?
display(N) :-
w(Order,_,Word,Class),
N > Order -> (write(Word), write(' '), write(Class)),
nl, fail .
Thanks in advance!
your problem is the way you use ->
First of all, the code is interpreted as:
display(N) :-
( w(Order,_,Word,Class),
N > Order )
) ->
( write(Word),
write(' '),
write(Class)
),
nl, fail .
-> destroys choice points meaning that it will not try to call w/3 again.
You could (probably) make it work like this:
display(N) :-
(w(Order,_,Word,Class),
N > Order )-> (write(Word), write(' '), write(Class)),
nl, fail .
but in the end it's really ugly code and, as you have seen, prone to bugs. A better way is to use forall/2:
display2(N) :-
forall(w(Order, _, Word, Class),
(N > Order ->
writef("%t %t \n", [Word,Class]); true)).
still, this will examine the whole database and print if N>Order. It is a bit unclear from your description if this is the desired behaviour but if you want to stop at the first element that is larger you could do something like:
display2(N) :-
catch((forall(w(Order, _, Word, Class),
(N > Order ->
writef("%t %t \n", [Word,Class]); throw(end))), end, true)).
not the most declarative way to do it but I'm not sure what's the best way to model it without knowing what w/4 is (I assumed that it is some prolog clauses but it could be a predicate accessing a DB though the ODBC layer)
I think you forgot the 'else' branch, and the precedence of (->)/2, that's higher that (,)/2, inhibits the intended order. Try
display(N) :-
w(Order,_,Word,Class),
( N > Order -> write(Word), write(' '), write(Class), nl ; true ),
fail .