278 karma · joined August 23, 2018
Essentially, it was hopeless to expect that designers would adapt their stylesheets to style headings based on the number of sections surrounding them. And if the visual styles don't adapt, nobody will use the outline algorithm to choose their headings.
(Which is sad, because the algorithm makes content nicely nestable. With the algorithm, I can take an article that was independent and use it as a section in a larger work, and the headings will automatically adapt. Though, as a pet-peeve of mine, nested IDs would also be somewhat necessary for this, so that it's fine to have two embedded articles which both have a section with an ID #introduction.)
The specification says that the syntax of expressions in this thing is `E ::= t | E E`. This is a bit confusing, because it might lead you to believe that all expressions just look like `t t t t t t t`. In reality, you are supposed to keep the implied bracketing, so expressions really look like `(t t) (t ((t t) (t t)))` (note that at the top level and in each parenthesis, there always exactly two subexpressions). Essentially, the space character becomes a binary operator (similar to how we often write multiplication with no symbol).
The expressions are a bit heavy on parentheses, so we say that this binary operator is left associative. This means that an expression `a b c` should be interpreted as `(a b) c`, an expression `a b c d` should be interpreted as `((a b) c) d`, and so on. If you think about it, this means that you can always get rid of an opening parenthesis at the left edge of an expression, i.e. we can assume that an expression always start without one.
With this out of the way, we can now understand where the trees come from: As there is only one terminal symbol, `t`, after removing unnecessary parentheses, every expression will always start with `t`, which is followed by a number of other expressions. To draw this as a tree, draw a node representing the initial `t`, and a sub-tree for each of the follow-up expressions (by applying the same procedure to them).
In this view, the semantic rules at the top of the specification page now tell you how to "simplify" a tree whenever there is a node with three or more sub-trees, or alternatively, how to reduce an expression that is a `t` followed by three or more sub-expressions. (In the syntax view, you replace the `t` and the first three expressions following it by whats on the right of the arrow. In the tree view, you replace the node and its first three children by some other sub-tree, then you attach the remaining children to the root of the new sub-tree.)
I know that there used to be some oversights where things where missing from the Law (like how to distinguish immediate effects from permanent effects on cards based on whether the box is paper or stone) but all of them have been resolved by now.
It's really just a bit unfortunate that (x, y) -> x**y is not continuous at (0, 0).
This is okay! They're students on the way to gain that experience. At some point you can and will go over to internalizing, instead. But as advice to students just starting out, memorizing is the way to go.
- It almost impossible to align the baseline of the formula to the baseline of the surrounding text.
- Often, images are only used for "complex" formulae, while simple ones are implemented using normal typesetting. This resolves the baseline issue for simple formulae, but now the fonts between simple and complex formulae don't match. (This requires extra concentration for the reader, as in other contexts, different font styles are frequently used meaningfully.)
- The images often have sub-par resolution.
The Hamiltonian cycle sequence from the original post is not a generator, but it visits every state. The question is: Is there a significantly shorter sequence that (when repeated) does the same?
The most common examples are empty collections: either disallowing them even though it would be possible to handle them, or making a strange choice like using vacuous falsity, i.e.
all [] == False
(Just for illustration what I mean by "vacuous falsity", Python's all correctly returns True).Now, every layer above has to special-case these as well, even if they would be a completely normal case otherwise.
I believe that to learn well, you need to be challenged, but not too much. Ability-based evaluation only does that for students whose abilities happen to line up with the expected standard. It is bad both for gifted students and for struggling students.
(Add another red marble if you want to alternate the starting team without switching their color and remove a red or blue marble each time.)
Also, just offering the ability to introduce structure does not mean that it is done. For example, I could still write
(p [I write this comment on August 1, 2024.])
So there is the question whether using something like Skribilo would really save us from having to heuristically extract dates from e-mail bodies in practice.(I volunteer at a polling station. We count the votes, and submit the raw counts to the next level. Then, we might do a quick calculation of the percentages, just to see how our voting district did. I can also go to my municipality's website and see the results of my polling station, and for the entire municipality. There are absolute numbers, which are reported to the next level, and the website also shows percentages, again, to gain insight into the municipality's results. Even if partial results are reported to the next level, this absolutely happens in the form of "these districts with this number of eligible voters have been counted; these are the absolute numbers".)
Everybody in the chain of responsibility should understand that the absolute numbers are what counts. And even if some people don't, the system must be set up in a way where you cannot transfer anything else. If there is even a serious possibility that someone might re-create voter counts from percentages, your system is a failure, and here it seems to have happened at the highest level.
Do this a couple of times with different videos, and you will start to build a repertoire of techniques yourself. At some point, you will be capable of solving puzzles on your own. (And if you get stuck, the video is there to help you.)
There just happen to be some types where you cannot change the referenced value in any way. These behave like value types in other languages.
(If you believe that they should be marked explicitly, do you also believe the same thing about Rust? I.e. should the Rust compiler throw an error if you never actually change a reference type, as you "should" be using a value type in that case?)
If you want mutable values in your language, you need to expect values to mutate. So what should list multiplication do?
- There is the current implementation, where a change to the first entry of [f(x)] * 4 will be reflected in all entries of the list (because they're all references to the same value). - It could only work on list literals. So [f(x)] * 4 would call f four times and collect the values in a list. Then, a = [f(x)]; a * 4 would not work, which would be surprising. - It could work differently on list literals and lists. So [f(x)] * 4 would call f four times collecting the results, but a = [f(x)]; a * 4 would just return a list of four references to the one result of f(x). - It could copy the value in the list. How would you do that if the list contained values like file handlers or network connections that can't be copied?
Within the context of Python (an imperative language with mutable values), the current implementation is the most sensible one. If you work in such a language, you must always exercise some care when working with mutable values. Learn your tool!
Python could have been designed with different decisions. But who knows if we would even be talking about it today in that case?
With your change, you calculate 1 // 0 instead, which crashes.
That said, I think checking isinstance(x, float) was always possible. (And nowadays, you can put a type annotation.)
That said, I don't really see why you would necessarily want float and integer division to behave the same. They're completely different types used for completely different things. Pick the one that is appropriate for your use case.
(It seems like abs(a % b) <= abs(b / 2) might be the right choice for floats which is pretty clearly not what you want for integers. I also just learned that integer division // can be applied to floats in Python, but the result is not an integer, for some reason?)