Skip to content
This repository was archived by the owner on Apr 6, 2020. It is now read-only.

Commit 8d4d077

Browse files
committed
docs: add pSet7/similarities
1 parent 10b20ab commit 8d4d077

25 files changed

+530
-2
lines changed

README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ This repository contains my solutions to each problem sets:
2424
- [Cash](./pSet6/cash/)
2525
- [Caesar](./pSet6/caesar/)
2626
- [Bleep](./pSet6/bleep/)
27-
- pSet7 →
28-
- pSet8 →
27+
- pSet7 → Flask & AJAX
28+
- [Similarities](./pSet7/similarities)
29+
- pSet8 → SQL
2930
- **Week 9 is off**
3031
- pSet10 →

pSet7/similarities/application.py

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import cs50
2+
import re
3+
from flask import Flask, abort, redirect, render_template, request
4+
from html import escape
5+
from werkzeug.exceptions import default_exceptions, HTTPException
6+
7+
from helpers import lines, sentences, substrings
8+
9+
# Configure application
10+
app = Flask(__name__)
11+
12+
# Reload templates when they are changed
13+
app.config["TEMPLATES_AUTO_RELOAD"] = True
14+
15+
16+
@app.after_request
17+
def after_request(response):
18+
"""Disable caching"""
19+
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
20+
response.headers["Expires"] = 0
21+
response.headers["Pragma"] = "no-cache"
22+
return response
23+
24+
25+
@app.route("/")
26+
def index():
27+
"""Handle requests for / via GET (and POST)"""
28+
return render_template("index.html")
29+
30+
31+
@app.route("/compare", methods=["POST"])
32+
def compare():
33+
"""Handle requests for /compare via POST"""
34+
35+
# Read files
36+
if not request.files["file1"] or not request.files["file2"]:
37+
abort(400, "missing file")
38+
try:
39+
file1 = request.files["file1"].read().decode("utf-8")
40+
file2 = request.files["file2"].read().decode("utf-8")
41+
except Exception:
42+
abort(400, "invalid file")
43+
44+
# Compare files
45+
if not request.form.get("algorithm"):
46+
abort(400, "missing algorithm")
47+
elif request.form.get("algorithm") == "lines":
48+
regexes = [f"^{re.escape(match)}$" for match in lines(file1, file2)]
49+
elif request.form.get("algorithm") == "sentences":
50+
regexes = [re.escape(match) for match in sentences(file1, file2)]
51+
elif request.form.get("algorithm") == "substrings":
52+
if not request.form.get("length"):
53+
abort(400, "missing length")
54+
elif not int(request.form.get("length")) > 0:
55+
abort(400, "invalid length")
56+
regexes = [re.escape(match) for match in substrings(
57+
file1, file2, int(request.form.get("length")))]
58+
else:
59+
abort(400, "invalid algorithm")
60+
61+
# Highlight files
62+
highlights1 = highlight(file1, regexes)
63+
highlights2 = highlight(file2, regexes)
64+
65+
# Output comparison
66+
return render_template("compare.html", file1=highlights1, file2=highlights2)
67+
68+
69+
def highlight(s, regexes):
70+
"""Highlight all instances of regexes in s."""
71+
72+
# Get intervals for which strings match
73+
intervals = []
74+
for regex in regexes:
75+
if not regex:
76+
continue
77+
matches = re.finditer(regex, s, re.MULTILINE)
78+
for match in matches:
79+
intervals.append((match.start(), match.end()))
80+
intervals.sort(key=lambda x: x[0])
81+
82+
# Combine intervals to get highlighted areas
83+
highlights = []
84+
for interval in intervals:
85+
if not highlights:
86+
highlights.append(interval)
87+
continue
88+
last = highlights[-1]
89+
90+
# If intervals overlap, then merge them
91+
if interval[0] <= last[1]:
92+
new_interval = (last[0], interval[1])
93+
highlights[-1] = new_interval
94+
95+
# Else, start a new highlight
96+
else:
97+
highlights.append(interval)
98+
99+
# Maintain list of regions: each is a start index, end index, highlight
100+
regions = []
101+
102+
# If no highlights at all, then keep nothing highlighted
103+
if not highlights:
104+
regions = [(0, len(s), False)]
105+
106+
# If first region is not highlighted, designate it as such
107+
elif highlights[0][0] != 0:
108+
regions = [(0, highlights[0][0], False)]
109+
110+
# Loop through all highlights and add regions
111+
for start, end in highlights:
112+
if start != 0:
113+
prev_end = regions[-1][1]
114+
if start != prev_end:
115+
regions.append((prev_end, start, False))
116+
regions.append((start, end, True))
117+
118+
# Add final unhighlighted region if necessary
119+
if regions[-1][1] != len(s):
120+
regions.append((regions[-1][1], len(s), False))
121+
122+
# Combine regions into final result
123+
result = ""
124+
for start, end, highlighted in regions:
125+
escaped = escape(s[start:end])
126+
if highlighted:
127+
result += f"<span>{escaped}</span>"
128+
else:
129+
result += escaped
130+
return result
131+
132+
133+
@app.errorhandler(HTTPException)
134+
def errorhandler(error):
135+
"""Handle errors"""
136+
return render_template("error.html", error=error), error.code
137+
138+
139+
# https://github.com/pallets/flask/pull/2314
140+
for code in default_exceptions:
141+
app.errorhandler(code)(errorhandler)

pSet7/similarities/compare

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/usr/bin/env python3
2+
3+
import argparse
4+
import re
5+
import sys
6+
7+
from helpers import lines, sentences, substrings
8+
9+
10+
def main():
11+
12+
# Parse command-line arguments
13+
parser = argparse.ArgumentParser()
14+
group = parser.add_mutually_exclusive_group(required=True)
15+
group.add_argument("--lines", action="store_true", help="compare lines")
16+
group.add_argument("--sentences", action="store_true", help="compare sentences")
17+
group.add_argument("--substrings", metavar="N", type=positive,
18+
help="compare substrings of length N")
19+
parser.add_argument("FILE1", help="file to compare")
20+
parser.add_argument("FILE2", help="file to compare")
21+
args = vars(parser.parse_args())
22+
23+
# Read files
24+
try:
25+
with open(args["FILE1"], "r") as file:
26+
file1 = file.read()
27+
except IOError:
28+
sys.exit(f"Could not read {args['FILE1']}")
29+
try:
30+
with open(args["FILE2"], "r") as file:
31+
file2 = file.read()
32+
except IOError:
33+
sys.exit(f"Could not read {args['FILE2']}")
34+
35+
# Compare files
36+
if args["lines"]:
37+
matches = lines(file1, file2)
38+
elif args["sentences"]:
39+
matches = sentences(file1, file2)
40+
elif args["substrings"]:
41+
matches = substrings(file1, file2, args["substrings"])
42+
43+
# Output matches, sorted from longest to shortest, with line endings escaped
44+
for match in sorted(matches, key=len, reverse=True):
45+
print(match.replace("\n", "\\n").replace("\r", "\\r"))
46+
47+
48+
def positive(string):
49+
"""Convert string to a positive integer."""
50+
value = int(string)
51+
if value <= 0:
52+
raise argparse.ArgumentTypeError("invalid length")
53+
return value
54+
55+
56+
if __name__ == "__main__":
57+
main()

pSet7/similarities/helpers.py

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
from nltk.tokenize import sent_tokenize
2+
3+
4+
def lines(a, b):
5+
"""Return lines in both a and b"""
6+
lines = []
7+
# Split string a into lines: \n
8+
# For each lines:
9+
for line in a.split("\n"):
10+
# Split string b into lines: \n
11+
# Check if line from a appears in b
12+
if line in b.split("\n"):
13+
# Check if the line is empty
14+
if line != "":
15+
# If not empty, append to list
16+
lines.append(line)
17+
return lines
18+
19+
20+
def sentences(a, b):
21+
"""Return sentences in both a and b"""
22+
sentences = []
23+
# Using sent_tokenize(text, language)
24+
# To divide string into sentences, EN language
25+
a = sent_tokenize(a, language='english')
26+
b = sent_tokenize(b, language='english')
27+
# For each sentence in a:
28+
for sentence in a:
29+
# Check if sentence from is also in b
30+
if sentence in b:
31+
# Check if sentence is empty
32+
# Check if not already in the list
33+
if sentence != "" and sentence not in sentences:
34+
# If not, append to list
35+
sentences.append(sentence)
36+
return sentences
37+
38+
39+
def substrings(a, b, n):
40+
"""Return substrings of length n in both a and b"""
41+
substrings = []
42+
a_substrings = get_substrings(a, n)
43+
b_substrings = get_substrings(b, n)
44+
# For every substring in list of substrings a:
45+
for a_substring in a_substrings:
46+
# For every substring in list of substrings b:
47+
for b_substring in b_substrings:
48+
# Check if substring from a is equivalent to substring from b
49+
# Check if substring from a is not already in substrings' list
50+
if a_substring == b_substring and a_substring not in substrings:
51+
# If not, append to substrings
52+
substrings.append(a_substring)
53+
return substrings
54+
55+
56+
# Get substrings of length n of string s
57+
def get_substrings(s, n):
58+
substrings = []
59+
for i in range(len(s) - n + 1):
60+
# string[start: end: step]
61+
substrings.append(s[i:i + n])
62+
return substrings
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
In the beginning God created the heavens and the earth. And the earth was waste and void; and darkness was upon the face of the deep: and the Spirit of God moved upon the face of the waters. And God said, Let there be light: and there was light.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
In the beginning, God created the heavens and the earth. The earth was without form and void, and darkness was over the face of the deep. And the Spirit of God was hovering over the face of the waters. And God said, "Let there be light," and there was light.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
In the beginning God created the heaven and the earth. And the earth was without form, and void; and darkness was upon the face of the deep. And the Spirit of God moved upon the face of the waters. And God said, Let there be light: and there was light.

pSet7/similarities/inputs/LesMis1.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
To love, or to have loved,this suffices. Demand nothing more. There is no other pearl to be found in the shadowy folds of life.

pSet7/similarities/inputs/LesMis2.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
To love or have loved, that is enough. Ask nothing further. There is no other pearl to be found in the dark folds of life.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"Goodbye," said the fox. "Here is my secret. It's quite simple: One sees clearly only with the heart. Anything essential is invisible to the eyes."
2+
"Anything essential is invisible to the eyes," the little prince repeated, in order to remember.
3+
"It's the time you spend on your rose that makes your rose so important."
4+
"It's the time I spent on my rose...," the little prince repeated, in order to remember.
5+
"People have forgotten this truth," the fox said. "But you mustn't forget it. You become responsible forever for what you've tamed. You're responsible for your rose..."
6+
"I'm responsible for my rose...," the little prince repeated, in order to remember.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"Goodbye," said the fox. "And now here is my secret, a very simple secret: It is only with the heart that one can see rightly; what is essential is invisible to the eye."
2+
"What is essential is invisible to the eye," the little prince repeated, so that he would be sure to remember.
3+
"It is the time you have wasted for your rose that makes your rose so important."
4+
"It is the time I have wasted for my rose--" said the little prince, so that he would be sure to remember.
5+
"Men have forgotten this truth," said the fox." But you must not forget it. You become responsible, forever, for what you have tamed. You are responsible for your rose . . ."
6+
"I am responsible for my rose," the little prince repeated, so that he would be sure to remember.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Raymond rang me up at the office. He said that a friend of histo whom he'd spoken about meinvited me to spend next Sunday at his little seaside bungalow just outside Algiers. I told him I'd have been delighted; only I had promised to spend Sunday with a girl. Raymond promptly replied that she could come, too. In fact, his friend's wife would be very pleased not to be the only woman in a party of men.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Raymond called me at the office. He told me that a friend of his (he'd spoken to him about me) had invited me to spend the day Sunday at his little beach house, near Algiers. I said I'd really like to, but I'd promised to spend the day with a girlfriend. Raymond immediately told me she was invited too. His friend's wife would be very glad not to be alone with a bunch of men.

pSet7/similarities/inputs/compare-1.c

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include <cs50.h>
2+
#include <stdio.h>
3+
4+
int main(void)
5+
{
6+
// get two strings
7+
string s = get_string("s: ");
8+
string t = get_string("t: ");
9+
10+
// compare strings' addresses
11+
if (s == t)
12+
{
13+
printf("same\n");
14+
}
15+
else
16+
{
17+
printf("different\n");
18+
}
19+
}

pSet7/similarities/inputs/compare-2.c

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include <cs50.h>
2+
#include <stdio.h>
3+
4+
int main(void)
5+
{
6+
// get two strings
7+
char *s = get_string("s: ");
8+
char *t = get_string("t: ");
9+
10+
// compare strings' addresses
11+
if (s == t)
12+
{
13+
printf("same\n");
14+
}
15+
else
16+
{
17+
printf("not same\n");
18+
}
19+
}
20+

pSet7/similarities/inputs/hello.c

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include <stdio.h>
2+
3+
int main(void)
4+
{
5+
printf("hello, world\n");
6+
}

pSet7/similarities/inputs/hey.c

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include <stdio.h>
2+
3+
int main(void)
4+
{
5+
printf("hey, world\n");
6+
}

pSet7/similarities/inputs/swap-1.c

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include <stdio.h>
2+
3+
void swap(int *a, int *b);
4+
5+
int main(void)
6+
{
7+
int x = 1;
8+
int y = 2;
9+
10+
printf("x is %i, y is %i\n", x, y);
11+
swap(&x, &y);
12+
printf("x is %i, y is %i\n", x, y);
13+
}
14+
15+
void swap(int *a, int *b)
16+
{
17+
int tmp = *a;
18+
*a = *b;
19+
*b = tmp;
20+
}

0 commit comments

Comments
 (0)