I'm trying to generate some latex code with prolog. I'm generating nodes connected by edges with the world they belong to. My code is this:
print_valutations(Stream,Number_of_nodes,[Head|Tail]):-
Number_of_nodes1 is Number_of_nodes+1,
single_valutation(Stream,Number_of_nodes1,Head),
print_valutations(Stream,Number_of_nodes1,Tail).
print_valutations(_,_,_):- !.
single_valutation(Stream,Number_of_nodes,[Head|Tail]):-
write(Stream, "\\node[circle, minimum size=6mm, draw]"),
write(Stream, "[left of= 10mm]"),
write(Stream, "[below of= "),
Number_of_nodes1 is Number_of_nodes-1,
write(Stream,Number_of_nodes1),
write(Stream,"]("),
write(Stream, Number_of_nodes),
write(Stream,"){"),
write(Stream,Head),
writeln(Stream,"};"),
val(Stream,Number_of_nodes,Tail).
single_valutation(_,_,_):- !.
val(Stream,Node,[Head|Tail]):-
display_val(Stream,Node,Head),
val(Stream,Node,Tail).
val(_,_,_):- !.
display_val(Stream,[]) :-
write(Stream,"").
display_val(Stream,Node,[Head|Tail]) :-
write(Stream,"\\draw[->] ("),
write(Stream,Node),
write(Stream,") to ("),
write(Stream,Head),
writeln(Stream,");"),
display_val(Stream,Node,Tail).
prova(FileName):-
open(FileName, write, Stream),
print_valutations(Stream, 100,[[a,[4]],[b,[3]],[c,[1]]]),
close(Stream).
And this code generates the following txt file:
\node[circle, minimum size=6mm, draw][left of= 10mm][below of= 100](101){a};
\draw[->] (101) to (4);
\node[circle, minimum size=6mm, draw][left of= 10mm][below of= 101](102){b};
\draw[->] (102) to (3);
\node[circle, minimum size=6mm, draw][left of= 10mm][below of= 102](103){c};
\draw[->] (103) to (1);
What i want is this(leaving left of = 10mm only for the first row):
\node[circle, minimum size=6mm, draw][left of= 10mm][below of= 100](101){a};
\draw[->] (101) to (4);
\node[circle, minimum size=6mm, draw][below of= 101](102){b};
\draw[->] (102) to (3);
\node[circle, minimum size=6mm, draw][below of= 102](103){c};
\draw[->] (103) to (1);
How can i do it?
First off I agree with Paulo that you should use DCGs.
Concept of answer.
One option is to add a guard around
write(Stream, "[left of= 10mm]")
e.g.
(
<guard>
->
write(Stream, "[left of= 10mm]")
;
true
)
See: Predicate ->/2
The guard needs to check when processing the first item in the list.
Working code.
Only the modified code is here. The rest stays the same.
It also threw a file error, but I didn't try to find and fix it as I did not modify the file access code.
The generated output is below.
print_valutations(Stream,First, Number_of_nodes,[Head|Tail]):-
Number_of_nodes1 is Number_of_nodes+1,
single_valutation(Stream,First,Number_of_nodes1,Head),
print_valutations(Stream,false,Number_of_nodes1,Tail).
print_valutations(_,_,_,_):- !.
single_valutation(Stream,First,Current_node_position,[Head|Tail]):-
write(Stream, "\\node[circle, minimum size=6mm, draw]"),
(
First
->
write(Stream, "[left of= 10mm]")
;
true
),
write(Stream, "[below of= "),
Current_node_position1 is Current_node_position-1,
write(Stream,Current_node_position1),
write(Stream,"]("),
write(Stream, Current_node_position),
write(Stream,"){"),
write(Stream,Head),
writeln(Stream,"};"),
val(Stream,Current_node_position,Tail).
single_valutation(_,_,_,_):- !.
prova(FileName):-
open(FileName, write, Stream),
print_valutations(Stream, true, 100, [[a,[4]],[b,[3]],[c,[1]]]),
close(Stream).
Output
\node[circle, minimum size=6mm, draw][left of= 10mm][below of= 100](101){a};
\draw[->] (101) to (4);
\node[circle, minimum size=6mm, draw][below of= 101](102){b};
\draw[->] (102) to (3);
\node[circle, minimum size=6mm, draw][below of= 102](103){c};
\draw[->] (103) to (1);
Related
Previously I wrote the predicate, reach(Departure, Arrivals) to find
all points I can get into from the Departure point. How I can find a
list of names of departure points from which I can get to a given
point arrivals?
For example ?- list(krum, Departure).
Answer: Departure = [uzhorod].
This is my code:
reachable(D, D, _).
reachable(Departure, Arrival, Visited) :-
trip(_, Departure, Point, _),
\+ member(Point, Visited),
reachable(Point, Arrival, [Point|Visited]).
reachable(Departure, Arrival) :-
reachable(Departure, Arrival, [Departure]).
reach(Departure, Arrivals):-
setof(
Arrival,
reachable(Departure, Arrival),
Arrivals
),
Arrivals \= [Departure].
This is my facts:
trip(01, kuiv, odessa, 1500).
trip(02, kuiv, lviv, 700).
trip(08, lviv, zaporizhya, 700).
trip(03, uzhorod, krum, 6000).
trip(04, vunohradiv, odessa, 2540).
trip(05, ternopil, kuiv, 3800).
trip(06, zaporizhya, donetsk, 900).
trip(07, lytsk, mariupol, 7500).
trip(Id, Departure, Arrivals, Price)
This is my output:
For example ?- reach(kuiv, Arrivals).
Answer: Arrivals = [donetsk, kuiv, lviv, odessa, zaporizhya]
As #TA_intern points out we just need to flip arguments in the reach:
inverse_reach(Arrival, Departures) :-
setof(
Departure,
reachable(Departure, Arrival),
Departures
).
So I have a list that looks like this:
[
["p1", "p2", "100", "Storgatan"],
["p1", "p3", "200", "Lillgatan"],
["p2", "p4", "100", "Nygatan"],
["p3", "p4", "50", "Kungsgatan"],
["p4", "p5", "150", "Kungsgatan"]
]
The elements in each nested list represent (in order):
1st element = Start Point
2nd element = End Point
3rd element = Distance
4th element = Street Name.
I have to now write a predicate which figures out which street is the shortest and which street is the longest, along with their respective (summed up) distances.
For example the final output should look something like this:
Longest street: Kungsgatan, 200
Shortest street: Storgatan, 100
I don't really understand why the start and end points are relevant information here. My current idea is to collect all the unique street names, put them in a separate list along with a counter for each street that starts at zero and then use that list to accumulate all of the distances for each separate street.
Something like:
create_sum_list([
["p1", "p2", "100", "Storgatan"],
["p1", "p3", "200", "Lillgatan"],
["p2", "p4", "100", "Nygatan"],
["p3", "p4", "50", "Kungsgatan"],
["p4", "p5", "150", "Kungsgatan"]
], SL).
SL= [[Storgatan, 0], [Lillgatan, 0],
[Nygatan, 0], [Kungsgatan ,0]]
accumulate(SL, List).
List=[[Storgatan, 100], [Lillgatan, 200],
[Nygatan, 100], [Kungsgatan ,200]]
This is probably a stupid idea and there is probably a way better way to solve this. I have thought of many different ideas where I either reach a dead end or they are way too complex for such a "simple" task.
I can achieve this easily through "normal" imperative programming but I am new to logical programming and Prolog. I have no idea how to achieve this.
Help?
Thanks!
If you already have a list and you want to group by street name and sum the lengths, you must decide how you do the grouping. One way is to use library(pairs):
streets_lengths(S, L) :-
maplist(street_name_and_length, S, NL),
keysort(NL, NL_sorted),
group_pairs_by_key(NL_sorted, G),
maplist(total_lengths, G, GT),
transpose_pairs(GT, By_length), % sorts!
group_pairs_by_key(By_length, L).
street_name_and_length([_, _, N, L], L_atom-N_number) :-
number_string(N_number, N),
atom_string(L_atom, L).
total_lengths(S-Ls, S-T) :-
sum_list(Ls, T).
You can use it like this:
?- streets_lengths([
["p1", "p2", "100", "Storgatan"],
["p1", "p3", "200", "Lillgatan"],
["p2", "p4", "100", "Nygatan"],
["p3", "p4", "50", "Kungsgatan"],
["p4", "p5", "150", "Kungsgatan"]
], SL).
SL = [100-['Storgatan', 'Nygatan'], 200-['Lillgatan', 'Kungsgatan']].
Since there can be many streets with the same length, the results are returned grouped by length. You can get the "shortest" and "longest" by getting the first and last element of the list, like this:
L = [First|_], last(L, Last)
Since this is homework I won't give you the entire answer but the key part of the code.
As I noted in the comments, the format of the structure matters, e.g. list, terms, atoms, strings, etc.
test(Street_lengths,Shortest) :-
List =
[
street(p1, p2, 100, 'Storgatan'),
street(p1, p3, 200, 'Lillgatan'),
street(p2, p4, 100, 'Nygatan'),
street(p3, p4, 50, 'Kungsgatan'),
street(p4, p5, 150, 'Kungsgatan')
],
street_lengths(List,Street_lengths),
lengths1(Street_lengths,Lengths),
min_list(Lengths,Min),
convlist(value_shortest2(Min),Street_lengths,Shortest).
street_lengths([H|T],Street_lengths) :-
merge_streets(H,T,Street_lengths).
% 2 or more items in list
merge_streets(street(_,_,Length0,Name),[street(_,_,Length1,Name),street(_,_,Length2,Name2)|Streets0],[street(Length,Name)|Streets]) :-
Length is Length0 + Length1,
merge_streets(street(_,_,Length2,Name2),Streets0,Streets).
merge_streets(street(_,_,Length0,Name0),[street(_,_,Length1,Name1)|Streets0],[street(Length0,Name0)|Streets]) :-
Name0 \= Name1,
merge_streets(street(_,_,Length1,Name1),Streets0,Streets).
% 1 item in list
merge_streets(street(_,_,Length0,Name),[street(_,_,Length1,Name)],[street(Length,Name)]) :-
Length is Length0 + Length1.
merge_streets(street(_,_,Length0,Name0),[street(_,_,Length1,Name1)],[street(Length0,Name0)|Streets]) :-
Name0 \= Name1,
merge_streets(street(_,_,Length1,Name1),[],Streets).
% no item in list
merge_streets(street(_,_,Length,Name),[],[street(Length,Name)]).
lengths1(List,Lengths) :-
maplist(value_length1,List,Lengths).
value_length1(street(Length,_),Length).
value_shortest2(Min,street(Min,Name),street(Min,Name)).
Example run:
?- test(Street_lengths,Shortest).
Street_lengths = [street(100, 'Storgatan'), street(200, 'Lillgatan'), street(100, 'Nygatan'), street(200, 'Kungsgatan')],
Shortest = [street(100, 'Storgatan'), street(100, 'Nygatan')] ;
false.
I left the longest for you to do, but should be a cake walk.
To display the information as you noted in the question I would use format/2.
So now you either have to change how you get the data read into the format for this code, or change this code to work with how you structured the data. IMHO I would change the data to work with this structure.
If want to know how efficient your code is you can use time/1
?- time(test(Street_lengths,Shortest)).
% 44 inferences, 0.000 CPU in 0.000 seconds (?% CPU, Infinite Lips)
Street_lengths = [street(100, 'Storgatan'), street(200, 'Lillgatan'), street(100, 'Nygatan'), street(200, 'Kungsgatan')],
Shortest = [street(100, 'Storgatan'), street(100, 'Nygatan')] ;
% 17 inferences, 0.000 CPU in 0.000 seconds (?% CPU, Infinite Lips)
false.
I have these code:
:- use_rendering(sudoku).
:- use_module(library(clpfd)).
sudoku(Rows) :-
length(Rows, 9), maplist(same_length(Rows), Rows),
append(Rows, Vs), Vs ins 1..9,
maplist(all_distinct, Rows),
transpose(Rows, Columns),
maplist(all_distinct, Columns),
Rows = [As,Bs,Cs,Ds,Es,Fs,Gs,Hs,Is],
blocks(As, Bs, Cs),
blocks(Ds, Es, Fs),
blocks(Gs, Hs, Is).
blocks([], [], []).
blocks([N1,N2,N3|Ns1], [N4,N5,N6|Ns2], [N7,N8,N9|Ns3]) :-
all_distinct([N1,N2,N3,N4,N5,N6,N7,N8,N9]),
blocks(Ns1, Ns2, Ns3).
problem(1, [[_,_,_,_,_,_,_,_,_],
[_,_,_,_,_,3,_,8,5],
[_,_,1,_,2,_,_,_,_],
[_,_,_,5,_,7,_,_,_],
[_,_,4,_,_,_,1,_,_],
[_,9,_,_,_,_,_,_,_],
[5,_,_,_,_,_,_,7,3],
[_,_,2,_,1,_,_,_,_],
[_,_,_,_,4,_,_,_,9]]).
%problem(1, Rows), sudoku(Rows), maplist(portray_clause, Rows).
I want to make a new main function that recieves as input a list of triads, in the form [[3,7,2], [5,1,9] ...], such that
each triad corresponds to a box inside the grid that already contains a value. For example,
for the case of the previous list, [3,7,2] means that the box in row 3, column 7, contains the
value of 2, and [5,1,9] indicates that the box in row 5, column 1, contains the value of 9
This is for my personal learning, thank you
I think you just need a predicate like this:
board_value([R,C,V], Board) :-
nth1(R, Board, Row),
nth1(C, Row, V).
Using it like this:
?- Board = [[_,_,_,_,_,_,_,_,_],
[_,_,_,_,_,3,_,8,5],
[_,_,1,_,2,_,_,_,_],
[_,_,_,5,_,7,_,_,_],
[_,_,4,_,_,_,1,_,_],
[_,9,_,_,_,_,_,_,_],
[5,_,_,_,_,_,_,7,3],
[_,_,2,_,1,_,_,_,_],
[_,_,_,_,4,_,_,_,9]],
board_value([5,2,1], Board),
write(Board).
[[_6,_8,_10,_12,_14,_16,_18,_20,_22],
[_24,_26,_28,_30,_32,3,_34,8,5],
[_36,_38,1,_40,2,_42,_44,_46,_48],
[_50,_52,_54,5,_56,7,_58,_60,_62],
[_64,1,4,_68,_70,_72,1,_74,_76],
[_78,9,_80,_82,_84,_86,_88,_90,_92],
[5,_94,_96,_98,_100,_102,_104,7,3],
[_106,_108,2,_110,1,_112,_114,_116,_118],
[_120,_122,_124,_126,4,_128,_130,_132,9]]
Board = [[_6, _8, _10, _12, _14, _16, _18, _20|...], [_24, _26, _28, _30, _32, 3, _34|...], [_36, _38, 1, _40, 2, _42|...], [_50, _52, _54, 5, _56|...], [_64, 1, 4, _68|...], [_78, 9, _80|...], [5, _94|...], [_106|...], [...|...]].
It may not be obvious, but the 5th row's 2nd column is now 1. Hope this helps!
My data looks like this:
> str(m)
int [1:8407] 930 1050 1225 1415 1620 1840 820 1020 1215 1410 ...
This is the time in hours and minutes. I'm trying to turn it into something (9:30, 12:10, 16:40, 8:25...).
> m1 <- strptime(m, "%H%M")
> head(m1)
[1] NA "2015-10-14 10:50:00 VLAT"
[3] "2015-10-14 12:25:00 VLAT" "2015-10-14 14:15:00 VLAT"
[5] "2015-10-14 16:20:00 VLAT" "2015-10-14 18:40:00 VLAT"
> str(m1)
POSIXlt[1:8407], format: NA "2015-10-14 10:50:00" "2015-10-14 12:25:00" ...
How to convert a set of digits in time?
Using regex:
sub("(\\d{2})$", ":\\1", x)
#[1] "9:30" "10:50" "12:25" "14:15" "16:20" "18:40" "8:20"
#[8] "10:20" "12:15" "14:10"
A match is made on the last two digits and adds a colon before it.
Data
x <- c(930, 1050, 1225, 1415, 1620, 1840, 820, 1020, 1215, 1410)
We format the numbers with sprintf to pad leading 0 for 3 digit numbers, use strptime and then use format to get the hour and min.
format(strptime(sprintf('%04d', v1), format='%H%M'), '%H:%M')
#[1] "09:30" "10:50" "12:25"
Or another option is
sub('(\\d{2})$', ':\\1', v1)
#[1] "9:30" "10:50" "12:25"
data
v1 <- c(930, 1050,1225)
Another way is,
x <- c(645, 1234,2130)
substr(as.POSIXct(sprintf("%04.0f", x), format='%H%M'), 12, 16)
#[1] "06:45" "12:34" "21:30"
I want to look whether words in my dataset appear in a certain text. When using grepl you only get exact matches. With agrepl it is possible tot do partial matching. However, I don't get the desired results with it.
Example data:
dt <- structure(list(id = c(0L, 0L, 0L, 0L, 0L, 0L, 0L, 1L, 1L, 1L, 1L, 1L),
words = c("weg", "verte", "spiegelend", "spiegeld", "einde", "spiegel", "spiegelende", "weg", "spiegelend", "asfalt", "fata", "morgana")),
.Names = c("id", "words"), row.names = c(NA, -12L), class = c("data.table", "data.frame"))
With:
dt <- dt[, .(id, words,
match1=mapply(grepl, words,
"hoe komt het dat de weg in de verte soms spiegelend lijkt"),
match2=mapply(agrepl, words,
"hoe komt het dat de weg in de verte soms spiegelend lijkt",
MoreArgs=list(max.distance=1L)))]
I get:
> dt
id words match1 match2
1: 0 weg TRUE TRUE
2: 0 verte TRUE TRUE
3: 0 spiegelend TRUE TRUE
4: 0 spiegeld FALSE TRUE
5: 0 einde FALSE FALSE
6: 0 spiegel TRUE TRUE
7: 0 spiegelende FALSE TRUE
8: 1 weg TRUE TRUE
9: 1 spiegelend TRUE TRUE
10: 1 asfalt FALSE FALSE
11: 1 fata FALSE FALSE
12: 1 morgana FALSE FALSE
As you can see, the results from grepl and agrepl differ on rows 4 and 7. However, I only want a match when there is at maximum one letter difference. The match in row 4 for match2 should therefore be FALSE. Changing parameters like max.distance or costs doesn't lead to the desired result either. Moreover, both matches on row 6 should be FALSE as well.
For example: for the word "spiegelend" from the text, the word "spiegelende" should give a match (only one letter difference), but the word "spiegeld" (two letters difference) and the word "spiegel" (three letters difference) should not give a match.
The conditions are allowed (but not at the same time):
one letter more (e.g.: "spiegelende" should give a match), or
one letter less (e.g.: "spiegelen" should give a match), or
one spelling error (e.g.: "spiehelend" should give a match)
Any ideas on how to solve this problem?
two ways to solve it, matching the approaches by nongkrong and RHertel:
dt <- cbind(dt[,c("id", "words")],
match1=mapply(grepl, dt$words,
"hoe komt het dat de weg in de verte soms spiegelend lijkt"),
match2=mapply(agrepl, dt$words,
"hoe komt het dat de weg in de verte soms spiegelend lijkt",
MoreArgs=list(max.distance=1L)),
match3=mapply(agrepl, paste0("\\b",dt$words,"\\b"),
"hoe komt het dat de weg in de verte soms spiegelend lijkt",
MoreArgs=list(max.distance=1L, fixed=F)),
match4=apply(adist( dt$words, unlist(strsplit("hoe komt het dat de weg in de verte soms spiegelend lijkt", split=" "))),
1, function (x) any(x<=1))
)
match3 uses the word boundary \\b, while match4 uses an edit distance (adist) of <=1 to single words in a vector
I thought about using adist() in this case with the condition < 2. But I'm not sure if it yields the expected output. Does this help?
idx <- which(adist(dt$words,dt2$words) < 2, arr.ind = T)
dt$match <- (dt$words %in% dt2$words[idx[,2]])
#> dt
# id words match
#1 0 weg TRUE
#2 0 verte TRUE
#3 0 spiegelend TRUE
#4 0 spiegeld FALSE
#5 0 einde FALSE
#6 0 spiegel FALSE
#7 0 spiegelende FALSE
#8 1 weg TRUE
#9 1 spiegelend TRUE
#10 1 asfalt FALSE
#11 1 fata FALSE
#12 1 morgana FALSE
data
dt <- structure(list(id = c(0L, 0L, 0L, 0L, 0L, 0L, 0L, 1L, 1L, 1L, 1L, 1L),
words = c("weg", "verte", "spiegelend", "spiegeld", "einde", "spiegel", "spiegelende", "weg", "spiegelend", "asfalt", "fata", "morgana")),
.Names = c("id", "words"), row.names = c(NA, -12L), class = c("data.table", "data.frame"))
dt2 <- structure(list(id = c(0L, 0L, 0L, 0L, 0L, 0L, 0L, 1L, 1L, 1L, 1L, 1L),
words = c("hoe", "komt", "het", "dat", "de", "weg", "in", "de", "verte", "soms", "spiegelend", "lijkt")),
.Names = c("id", "words"), row.names = c(NA, -12L), class = c("data.table", "data.frame"))