-
-
Notifications
You must be signed in to change notification settings - Fork 403
New practice exercise sgf-parsing
#795
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Thank you for contributing to Based on the files changed in this PR, it would be good to pay attention to the following details when reviewing the PR:
Automated comment created by PR Commenter 🤖. |
Honestly, this is a bit above my knowledge level 🙈. I'm just a simple web developer who doesn't know fancy algorithms... it's embarrassing but I have successfully avoided solving the
That's a good call because you couldn't use an external dependency in a solution anyway 😉
It works, it fulfills the requirements, I don't understand it at all so I can't say more 😅 but it's fine with me
That probably means we should require all of the usual things :) difficulty level 8 also sounds reasonable to me. I'm only conflicted about the Nitpick: there's some wild |
B\[aa\]. (Black plays on the point encoded as "aa", which is the | ||
1-1 point (which is a stupid place to play)). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this really in the problem specs instructions?
B\[aa\]. (Black plays on the point encoded as "aa", which is the | |
1-1 point (which is a stupid place to play)). | |
B\[aa\]. (Black plays on the point encoded as "aa", which is the | |
1-1 point). |
If it is, I think this needs an upstream PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally find it pretty amusing :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ya, but we've made a conscious effort to remove this type of comment/test case from problems.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's probably the right decision. What do I do for now? Remove it here and you do a PR? Or do we wait for your PR?
Is there a reason why the specifications are duplicated in the tracks repositories? I would think that a central one might work best, with appendices added by the tracks as necessary, which is kind of what's happening now anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Problem specs will take time to approve, and afaik it doesn't sync yet. I think there is a copy for the same reason as right now it doesn't pull the instructions centrally. I think this is future work to be done, just hasn't been prioritized ahead of other product work for v3.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool stuff, I've seen and used NimbleCSV and NimbleParsec before never implemented it myself. Left a couple comments just what I thought of the example while reading it. Given it is a proof of solvability, all non-blocking for me.
Great work
lift2( | ||
&{&1, &2}, | ||
some(satisfy(&(&1 not in '[();'))) | ||
|> map(&Enum.join(&1, "")) | ||
|> validate(&(&1 == String.upcase(&1)), "property must be in uppercase"), | ||
some( | ||
char(?[) | ||
|> error("properties without delimiter") | ||
|> drop_and(many(escaped(&(&1 != ?])))) | ||
|> drop(char(?])) | ||
|> map(&Enum.join(&1, "")) | ||
) | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be easier to read/understand this block if it were split apart,
lift2( | |
&{&1, &2}, | |
some(satisfy(&(&1 not in '[();'))) | |
|> map(&Enum.join(&1, "")) | |
|> validate(&(&1 == String.upcase(&1)), "property must be in uppercase"), | |
some( | |
char(?[) | |
|> error("properties without delimiter") | |
|> drop_and(many(escaped(&(&1 != ?])))) | |
|> drop(char(?])) | |
|> map(&Enum.join(&1, "")) | |
) | |
) | |
node_property = | |
some(satisfy(&(&1 not in '[();'))) | |
|> map(&Enum.join(&1, "")) | |
|> validate(&(&1 == String.upcase(&1)), "property must be in uppercase") | |
node_value = | |
some( | |
char(?[) | |
|> error("properties without delimiter") | |
|> drop_and(many(escaped(&(&1 != ?])))) | |
|> drop(char(?])) | |
|> map(&Enum.join(&1, "")) | |
) | |
lift2(&{&1, &2}, node_property, node_value) |
with {:ok, result, rest} <- parser.(string) do | ||
if p.(result) do | ||
{:ok, result, rest} | ||
else | ||
{:error, err, rest} | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When there is a single expression in the with
, is there a benefit to using with
over just a match?
with {:ok, result, rest} <- parser.(string) do | |
if p.(result) do | |
{:ok, result, rest} | |
else | |
{:error, err, rest} | |
end | |
end | |
{:ok, result, rest} = parser.(string) | |
if p.(result) do | |
{:ok, result, rest} | |
else | |
{:error, err, rest} | |
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The with
here is important, because I need the error to be passed further if parser.(string)
fails. If I use {:ok, result, rest} = parser.(string)
and the parser fails, I will get an exception.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
The |
For beginner/intermediate, which is what we are likely to see on exercism, I would guess that most people will end up using a combination of string lib functions and regex. |
Yes, I'm ashamed 😳
OK, so leaving
Give it a try, it's not that hard, because you just need to refactor existing code :)
OK, I will remove errors from the list. But for this particular exercise, handling errors was actually tricky, and I would call that proper handling practice, so I wish we had a concept for that. |
Maybe it's a topic we could explore more as part of the Speaking of which, if (when? 😇) we ever add that |
I think we should touch on it with the However, I'm not sure this exercise is the best to link with the |
It would be great to have more help with concepts and concept exercises 🙂
|
It looks to me like @neenjaw's feedback has been all addressed, so I'm merging 🎉 |
New one!
This one might need a bit of discussion because I went a little nuts here. I love using parser combinators in Elm and Haskell, so whenever I see a slightly non-trivial parsing problem, I want to use that. I believe there are such libraries in Elixir (NimbleParsec judging from this article that inspired me) but I felt like building my own, so I did.
It was super fun, but I feel that most people will not use that technique, they would probably use regex or something. What is the go-to parsing method in Elixir?
In any case, this might be acceptable because it's only an example implementation, but that makes it hard to pick good prerequisites and a difficulty level. What do you think?