Hacker Newsnew | past | comments | ask | show | jobs | submit | ancientstraits's commentslogin

The "how to implement a hash table" article https://benhoyt.com/writings/hash-table-in-c/ was really helpful for me. I thought that hash tables were something that were basically impossible to make in C, but this showed that it was simpler.


You can also look at how CPython implements hash tables in C. The implementation is surprisingly approachable for a real world one.


SDL3's properties API uses an internal hashtable I'm waiting for them to make public.

I also like https://github.com/tidwall/hashmap.c for general use.



Why? The only thing missing is the proper data structures and algorithms background.

Which during my degree, the lab deliverables were 100% C code.

Here, one possible book:

Data Structures, Algorithms, and Software Principles in C (1994 edition)

https://www.amazon.com/dp/0201591189


When I was in university, hash tables were definitely among the things we had to learn about and then implement in C, probably in first year. Per Wikipedia, the concept dates to 1953 (with an implementation in assembly); of course people made them in C once that was an option.


I always liked that idea of just doing a linear wrap-around sweep through an array that holds (key, value) pairs, until you find the pair with the key you're looking for (for 'get' or 'contains?') or an empty spot (for 'put'). The trick to make this fast is that the hash function (key -> int) tells you at which index to start the sweep.

No secondary data structures, super simple to implement, and it works great for small use cases.


This isn't great though if you need to find out if an element exists in the table, and you have the ability to remove elements from it. I suppose you could optimize the hash table after deletions, but that again would be slow.. ?

Btw, this reminds me a bit of Cuckoo hashes. Never used them but seems like a nice idea.


The expected or amortized complexity of the standard hash table operations (put, get, contains, remove) are all O(1) for linear probing (under the usual assumptions for the hash function and as long as the table does not get too full). Of course that doesn't shield a single operation from being O(n) in the worst case.


The standard hash table has buckets, though, not just a single array that is probed through in case there's no space for a value in the slot determined by the hash function?


Right, that's the beauty of this simple approach: there's only one array and it doubles as the storage for buckets. But yes, the table will have a maximum size after which you cannot add more entries. That is, in practice you would create a new hashtable with a larger capacity and rehash all the existing entries into the new, bigger table (in practice, you would do that even sooner than that, namely when a certain load factor is passed - see my previous comment where I alluded to the role of the load factor).

The cool thing, however, is: if the size of the new hashtable is double the size of the old hashtable, your amortized insertion costs are still only O(1)!

(And you don't just have to take my word for it: take my original comment and paste it into the AI interface of your choice and have it create a concrete implementation. Ask it to add a remove operation, and an automatic doubling of the array size + rehashing when the table reaches a load factor of, say, 0.7 -- the resulting code should be very manageable, and then you can run your own tests and measure times!

This is maybe not the smartest way to do hashing, but its appeal lies in its simplicity and hence compactness of implementation. There are many cases where you don't even need a 'remove' operation, and where you never have to worry about growing the array because you know that you're only ever going to hash a certain number of elements at most.)


Let's say we have removed all elements from the hash. Then all following contains -calls will need to be O(n), if I understood correctly? They need to check every slot in the array to confirm inexistence, rather than just one bucket.


There are different ways on how to implement remove, but one way to do it is to overwrite the (key, value) pair to be removed with a special "tombstone" symbol that is different from null. Another way is to shift all elements with the same hash value as the one to be removed one index to the left, and null the final position.

There are cases where contains or get would have to iterate over the whole array, basically when all the previously added elements hash-collide, i.e., are mapped to the same start-index. But every hash-implementation has pathological cases where the access methods get slow -- but if the keys are sufficiently random, you don't expect such cases to occur in practice.


> Another way is to shift all elements with the same hash value as the one to be removed one index to the left, and null the final position.

I suppose that would work, but I can't recall ever hearing of an implementation that actually does that.


If hash tables were impossible to make in C, then it would be impossible to make them in Python, because it is a higher level language than C.

(Almost by definition, anything doable in a higher level language is doable in a lower level language, but not necessarily vice versa. In fact, many higher level languages are themselves written in lower level languages, e.g. Python is written in C.)

But Python has got dicts built into it, which are nothing but hash tables, and they are almost certainly written in C.

Google for some videos by Raymond Hettinger about Python dictionaries.

Or look at the source code of the Python interpreter.


> But Python has got dicts built into it, which are nothing but hash tables, and they are almost certainly written in C.

In CPython they are written in C (at the moment). Other Python implementations are written in other languages.

> (Almost by definition, anything doable in a higher level language is doable in a lower level language, but not necessarily vice versa. In fact, many higher level languages are themselves written in lower level languages, e.g. Python is written in C.)

It depends on what you mean by 'anything doable'. Eg Haskell compilers can in principle do lots of crazy optimisations that a C compiler would not be able to safely do, just because they don't have enough information. Even more so for Lean compilers, which can _know_ which of your loops are terminating, instead of making crude assumptions like C compilers.

Btw, higher level languages being implemented in lower level languages is mostly something for interpreters. Writing a C interpreter in Python is pretty much futile, if you care about speed. But writing a C compiler in Python is perfectly fine. And writing a Python compiler in Python is also fine. Many languages self-host (at least some of) their compilers.


>In CPython they are written in C (at the moment). Other Python implementations are written in other languages.

True. E.g.:

1. PyPy:

https://en.wikipedia.org/wiki/PyPy

>The PyPy interpreter itself is written in a restricted subset of Python called RPython (Restricted Python).

2. Jython:

https://en.wikipedia.org/wiki/Jython

>Implementation language: Python, Java

The article says the last release of Jython was 2 years ago. I wonder how much it is used these days. It did seem like a good idea when it was first released. I had only tried it out a little at that time.


>It depends on what you mean by 'anything doable'. Eg Haskell compilers can in principle do lots of crazy optimisations that a C compiler would not be able to safely do, just because they don't have enough information. Even more so for Lean compilers, which can _know_ which of your loops are terminating, instead of making crude assumptions like C compilers.

Interesting. What are some of those optimisations that Haskell and Lean can do, that a C compiler would not be able to safely do?


A very simple one: in Haskell the compiler has knowledge about which functions cause side-effects and which don't. The compiler can skip calling pure functions whose return value ain't used; similarly, the compiler can call a pure function multiple times, instead of saving the return value in memory.

The Haskell compiler can also fairly freely re-arrange in what order things are computed.

A fairly common idiom in Haskell is to just return a tuple of two values, if they are 'thematically' linked; even if computing one is much more expensive than the other. The compiler is smart enough, to only compute one of the values, if you only use one of them. (When I say 'tuple', it could be literally (a, b), or it could be in a struct or whatever.)

Lean knows even more about your code, so you could do more optimisations. But you need to ask Claude for details, I haven't used it as much nor thought as much about it.


Thanks.


IIRC, the first edition of the book "The C Programming Language" (aka K&R), by Kernighan and Ritchie, had a simple example of how to create a hash table in C, including with buckets to handle collisions. It was a simple algorithm. It simply added up the ASCII values of all the characters in the key, modulo some number.


> I thought that hash tables were something that were basically impossible to make in C,

Why would you believe this in the first place?


Yeah it's a strange assumption, as this was taught in basic data structures class years ago and any language can implement any data structure, technically speaking. C especially is a weird assumption because lots of foundational software is written in C including hash tables.


Arrays are hash tables. You can implement a very simple hash table from a tutorial, but can you implement a sophisticated one? What about a concurrent hash table?


Yeah, I'm pretty sure we made hash tables in the first C class I took in college in my second semester freshman year. If you can make a linked list, and then make an array of them, and a function to map keys to array indexes, you have a hash table. Whether it's actually performant is entirely a separate question, but a naive hash table is still a hash table.


Hash tables are awesome. They are both an incredibly simple data structure and a seriously deep rabbit hole. Most of the complexity comes from collision resolution [1], and how you handle resolution largely determines what kind of hash table you have. There are at least dozens of collision resolution approaches. The simplest, and probably the one you implemented in your undergrad C class was open addressing. That’s also what I implemented as an undergrad. But there are many more approaches, some quite a bit more complicated, and many of them let you continue to shave off asymptotic costs when you run collision resolution, or they improve locality for typical lookups, allowing better cache utilization, etc. Hash tables are super fun to play with, and for full effect, you really do need to implement them in something like C.

I only skimmed the linked article, but I do wonder whether the author ever realized that they needed to think about scope rules. I searched for the word “scope” but never found it. Closures seriously complicate language design and things get painful and counterintuitive unless you use lexical scope (or… you know… you like pain).

[1] https://en.wikipedia.org/wiki/Hash_table#Collision_resolutio...


Yeah, I learned about a bunch of the other algorithms later (forgetting the names, but stuff like "move to the next slot rather than putting collisions into buckets" and "hash a second time if you hit a collision"; I'm sure I'm forgetting some of the nuances).

> Closures seriously complicate language design and things get painful and counterintuitive unless you use lexical scope (or… you know… you like pain)

Well if you like both lexical scope and pain, there's always lisp!


Be careful with Lisp!

You might end up founding one of the first e-commerce sites, sell it for a handsome payday, and then create the first startup accelerator and make insane amounts of money while transforming the industry.

Safer to stick to Blub.


I'd need to solve time travel first for that, but I guess lisp is as good a language as any for figuring that out


> Arrays are hash tables.

Maybe in JavaScript... but there is a topic called datastructures, where arrays are arrays, and hashtables are hastables. If people say this without a flip of an eye I'm not surprised why people write stuff like

> I thought that hash tables were something that were basically impossible to make in C


In a trivial sense, arrays are hash tables with a hash function of the identity f(x)=x.

It’s just not particularly common (or helpful) to view them that way.


In LeetCode, it's a very common optimization. If your key values are dense, you use an array. If your key values are sparse, you use a dict.


Besides the sibling comment, you can used closed hash tables algorithm (open addressing), which is fully based on a single array.

https://en.wikipedia.org/wiki/Open_addressing


I have been using Audacity 3 for making music with samples for a few years. And it sort of worked, but there were a lot of things that were inconvenient, like the project sometimes not saving, or getting a "clicking" noise across clips that I had to remove by manually fading in/out each end of the clip.

I tried the Audacity 4 beta a while ago, and it seemed super clean and like it fixed a lot of stuff. I am pretty concerned about the encroaching audio.com things (even though they are completely optional), but I still think this Audacity version has promise. I especially like how you can drag clips to time-stretch them.


> or getting a "clicking" noise across clips that I had to remove by manually fading in/out each end of the clip

Are you sure this isn't just the typical "clip/crop a sample at the wrong moment, in the middle of a soundwave, and you'll get a pop/click"? This problem happens all across DAWs and hardware samplers and such, usually there is a way to "align to zero-crossings" when you wanna avoid it, or called something similar. I'd be surprised if Audacity didn't have that optionally since before, or that they didn't have it before but now with Audacity 4 it would suddenly be used automatically, that'd be somewhat strange too.


At least with Reaper, if you cut an audio clip it will, by default, automatically apply small adjustable fade in/out envelopes to the ends so you specifically don't have to manually do it.


Yeah, every software/hardware seems to deal with this differently. The sampler I use the most (Octatrack) only jumps by zero-crossings when holding SHIFT, otherwise lets you freely create entirely new pops and cracks in your songs :)


I think you're right. Audacity does have a "zero-crossings" thing, but when I tried using it a long time ago, it didn't seem to work, and I just learned to do fade-in/out for everything. Maybe I did something incorrectly.


> I just learned to do fade-in/out for everything. Maybe I did something incorrectly.

This is the de-facto standard practice for fixing/working around that particular issue, so I don't think it's incorrect at all :) Besides, however you fix an issue and it sounds good, it's correct :)


Yep that was my thought as well. Most DAWs default to a very quick cross fade when joining two clips for this very reason.


This is a very unexpected way to try combatting addiction. I don't know how well it will work, but I will consider it.


Negative reinforcement. There's a strategy for smoking where you put a wad of hair in your cigarettes. I used nicotine patches myself, so I can't speak to the efficacy.


Nit: that's not what negative reinforcement means. Negative reinforcement is about removing a negative stimulus, like inducing someone to go to a desirable website by improving their initially bad text contrast whenever they go there.

In this case, jumpscaring yourself would just be considered punishment (or "positive punishment").


To re-frame it as a list of combinations:

* Positive reinforcement: [Adding] something so that entity does the action [more]

* Negative reinforcement: [Removing] something so that entity does the action [more]

* Positive punishment: [Adding] something so that entity does the action [less]

* Negative punishment: [Removing] something so that entity does the action [less]

P.S.: Note that this intentionally avoids diving into exactly how the Entity judges the Something. It's not always clear, even if in many cases you can guess.


P.S.: Sharing a book-quote that seems apropos, particularly the final two lines.

> People came, and tormented a nameless thing without boundaries, and went away again. He met them variously. His emerging aspects became personas, and eventually, he named them, as well as he could identify them. There was Gorge, and Grunt, and Howl, and another, quiet one that lurked on the fringes, waiting.

> [...] Howl handled the rest. He began to suspect Howl had been obscurely responsible for delivering them all to [the torturer] in the first place. Finally, he'd come to a place where he could be punished enough. Never give aversion therapy to a masochist. The results are unpredictable.

-- Mirror Dance by Lois McMaster Bujold


There's also Allen Carr's books about treating addiction, and they don't use negative reinforcement, at least the ones I've read

Rather it helps you learn to recognize the fallacies behind the addictive cravings themselves, and to thus resolve the core of why you turn to that in the first place

Still have to make the decision to recall those in the moment, but when you do you do neutralize the cravings

His first book was Easy Way to Stop Smoking

For digital addiction there's Smart Phone, Dumb Phone

For internet porn there's easypeasymethod.org (based on EasyWay to Stop Smoking)


I know two people that quit with that book, both more than 20 years ago. One restarted few years ago, the other one never smoked again.


I put wads of toenail clippings like ))))) in my Lisp code.

Didn't work; still at it, 25 years and counting.


I'll admit that I went quite far in your comment history to figure out if maybe those 5 closing parentheses were matching some long-lost post of yours, and as a Lisp addict you couldn't resist the temptation.

Alas, I am disappointed to inform you that your comment history will no longer compile :(

:)


I genuinely hope you find a way to quit. you made me do an audible gag sound.


> toenail clippings like ))))) in my Lisp code

I smell a custom font opportunity.


Yeah, counting parens! Dohohoho!


Go Parinfer, never look back!


As someone who has smelled burning hair, it at least sounds plausible. On the other hand, cigarette smoke already doesn't smell good.


people who smoke on the daily have already tuned out cigarette smoke mentally, the burning hair however is rancid to anyone.


Definitely not always true. I smoke, I hate it, I've tried to quit several times. The smoke smell has never repulsed me but I find it to smell terrible. Many people I know who smoke are the same.

It's a constant reminder that you're killing yourself for miniscule amounts of Feel Good chemicals at a time.


I've always enjoyed the smell of tobacco smoke. It's nowhere near as astringent and repulsive as woodsmoke and good tobacco often has a nice nearly floral scent or a sweet smell. The taste and the tearing up my throat and making my breath bad and the expense are all things I can do without though.

Does anyone else get seasonal nicotine cravings? In the warmer months, I don't even think about smoking unless I drink, but in winter I often can't sleep for craving a cigarette, even when it's been literally years since the last one.


Definitely. I'm usually quite successful at quitting during the summer. Then the winter months come and for some reason the urges come back.


That is the lie you tell yourself. But smoking doesn't give you Feel Good chemicals. It temporarily resets the clock of slowly building Feel Bad chemicals back to zero or back to lower. Its basically the same as saying: releasing that string - that I wound around my big toe - every once in a while gives me Feel Good chemicals. You are just relieving stress that has been created by the tobacco.


This isn't true at all, though. Nicotine is strongly dopaminergic, it directly produces euphoria.

You can describe the trap which is addiction without saying things which are obviously not true. Or, to return your uncharitable rhetoric back upon you, lying.


Ah yes, I lied to myself. Thank you for fixing things for me.


Use nicotine pouches or vape, there is no reason to smoke and nicotine isn't carcinogenic


Thanks but I'm not really looking for advice. Where I live vape juice is exorbitantly priced. Plus there are other habitual issues with vaping, such as vaping indoors which overall increases my intake. Pouches aren't always a solution either and don't solve the "habitual" part of the habit, which is the hardest part to kick for me.


That's why the pouches are good. They reduce your craving, therefore making the act of smoking seem less necessary. Of course, the act itself is still appealing, but then you can work on reducing that habit independently and perhaps saving it for special occasions. I find cigars quite good for this. Too expensive and fancy to smoke every day but you can really treat yourself on your birthday or on holiday with something like a cigar.


Pay the higher cost for the vape or you pay with your life. If intake is increase because you will do it indoors it won't really matter, it isn't nearly as bad.


I'm aware, thanks.


Nicotine pouches are okay, I would advise against vape though. Vape is honestly more addictive than cigarettes and I find, in large volumes, the vapour still affects my lung function. Nicotine pouches or gum are the way to go to get the benefits with minimal side effects.


I had the same experience. I vaped way more than I smoke, and it really is the nicotine that affects me the worst in the mid term. Pouches are expensive here after the tax hike and they make me feel dizzy and the gum makes me sick. I also scarred from a patch (my fault, I was dumb and left it on over a transatlantic and it chemical burned me) and they don't always work for me for some reason. Though they're my go to for when I try to quit.

I think the habit of actually smoking is the hardest for me to kick. The small break is what keeps me going and clears my head, especially during work, and it's hard to replicate that.


vapes aren't as cool as smoked tobacco :)


Cigarette smoke is vile, but burned hair is another level.


To that I raise: crude oil

Unbelievable how its products separately don't smell nearly as bad.


I’ve never experienced crude oil firsthand, but I assume you are talking about “sour crude” which has a high sulfur content, including hydrogen sulfide, resembling rotten eggs or raw sewerage. So-called “sweet crude”, with a low sulfur content, has a less offensive smell, smelling more like the petroleum products derived from it.


> where you put a wad of hair in your cigarettes

Oh god I almost gagged just thinking about it. If negative reinforcement works, that'd do it haha


SNRIs like Wellbutrin do this. They make cigarettes taste gross.


It is hilarious that page shows how one replaces FB with HN... which is precisely what I did in 2019, and now I'm a total HN addict, spending more than a few minutes on it at least 5 times a day.


In case you don't already know - set noprocrast in your HN profile to limit the time you spend on this site.


in case you don't know this only has limited effect, cause you can open it again in a private browser. besides the search works with the procast setting, and we're not talking external constraints, people, us, me,... lack at this point internal mental defenses against the negative impact of social media.


See, this is the problem with all these mindfulness tools. I, the human, can simply defeat every challenge the computer attempts. And i require the ability to do so in case i have a need that's only satisfied by searching reddit for textbook recommendations while I'm in half price books, or a youtube video to fix my broken zipper pocket before i head to the gym soon. The fact that they time wasting sites are also the "goldmine of knowledge" sites make me include a back door in any time minder tool. A backdoor which is only used when needed... until it slowly becomes muscle memory and the tool is defeated by my capacity to learn to fuck myself over again and again.


Instead you ought to pay someone $15/hr to sit next to you. Anytime you're distracted by a website for more than five minutes, they punch you in the arm.

They also have to know they don't get paid if you manage to convince them to go away and leave you alone for a while.


Someone should to invent an AI powered one that is utterly unpredictable and installs in the boot sector. You're browsing Hacker News one day and suddenly your smoke alarms goes off. You do it the next day and your Echo device starts playing death metal at highest volume. You do it the next day and your car alarm goes off.


It unsettled me a lot about just how much work was put into making the JavaScript version of this work instead of a purely Python version, due to how OpenCV works. I wonder how universal the laggy OpenCV thing is, because my friend faced it too when working on an OpenCV application. Is it so unavoidable that the only option is to not use Python? I really hope that there is another way of going about this.

Anyways, I am very glad that you put in all that effort to make the JavaScript version work well. Working under limitations is sometimes cool. I remember having to figure out how PyTorch evaluated neural networks, and having to convert the PyTorch neural network into Java code that could evaluate the model without any external libraries (it was very inefficient) for a Java code competition. Although there may have been a better way, what I did was good enough.


Creating a faster python implementation can definitely be done. OpenCV is a thin wrapper over the C++ API so it's not due to some intrinsic python slowness. It is not easy to resolve though and I suspect the way python code is typically written lends itself to an accidentally blocking operation more often than JS code. It's hard to know without seeing the code.


author here, sorry you have to see my janky JavaScript solution XD but one good thing of going with Tauri is that developing the UI is pretty easy, since it's basically just some web pages, but with access to the system, through the JS <-> Rust communication.

also, rewriting neural network from PyTorch to Java sounds like a big task, I wonder if people are doing ML in Java


A few weeks ago, I tried to compile Clang to WebAssembly, but got several different errors, and tried fixing a lot of them, but some of them seemed kind of impossible to fix, so I thought I would try again at a later date. However it seems I will not need to try again. I feel angry that someone made a convenient solution before I did, but also happy, because this probably implies that they made a consistent process to compile Clang for WASM.


Hmm, I wonder why `-pattern_type glob` doesn't work on Windows. Perhaps it is something that could easily be programmed into the source code?


If I were yo guess, it might be using the GNU libc (or compatible) glob functionality under the hood.


I looked up what "the sentinel" was, and it turns out it was a game in 1986, that works on the BBC Micro and Commodore 64, and has 3D graphics! I didnt even know it was possible to do that on average computers in 1986!


There was no texture mapping, and the 'trick' that makes it work is that all you can do is rotate/pan in place. The only movement is instantaneous teleportation.


It's still an amazing achievement.

The world procedural generation from a seed alone is amazingly well implemented in an environment so constrained most programmers nowadays wouldn't even be able to do anything in.


Leaderboard had a polygonal golf course. The course was flat, but drawn in a 3d perspective with billboard trees.

Stunt car racer had a 3d course and vehicle physics.

Elite had 3d space flight.

https://www.youtube.com/watch?v=C0w1e_kzLDo


Oh c'mon we had a full flight simulator in 128x48 monochrome in 1979!

https://en.wikipedia.org/wiki/FS1_Flight_Simulator


True, but the CPU the C64 used (MOS 6502) was introduced in 1975. Also the flight simulator was wireframe while Sentinel was solid 3D.


Wireframe, not full screen polygon-fill like Sentinel.


The online SAT was much easier than the written version, because (1) You don't have to read big essays anymore in the reading part, and (2) both math sections have the Desmos graphing calculator, with absolutely no features taken out. Desmos is far more convenient to use than a TI-84 for many things, so it made all the problems, especially the hard ones, much easier.

Also, in my opinion, their Bluebook testing software is actually pretty good, especially compared to other digital testing software I had to use over the years, like ClearSight.


Of course, in the real world, people are more likely to use desmos than a TI-84; the test conditions matching reality is a good improvement.


I am so impressed that you were able to figure so many things out in the puzzle! I feel like I would never be able to do that, but I feel like all that is needed is a lot of patience, a lot of time, and a lot of deep thought.


Yes! OP is far more clever than me. I used to think I was a smart person but it takes regular reminders like that to assure me I'm average at best!


Perhaps someone should make a monthly HN magazine, with the top articles and a "Who's Hiring?" section.


I would subscribe to Byte, DDJ, Creative Computing, and Scientific American (pre-1990 to be generous, maybe pre-1970 if I'm not), if their equivalents existed today.


Someone did, but I think it didn't survive: https://archive.org/details/hackermonthly-issue1

Maybe because too many people are mostly interested in the comments?


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: