Open Side Menu Go to the Top
Register
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** ** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD **

07-20-2018 , 11:26 PM
Quote:
Originally Posted by suzzer99


I don't think this can be topped.
Not sure if this tops it. Found a job post a while back offering 40k for a software developer position in Seattle. Wat.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
07-21-2018 , 03:14 AM
Quote:
Have a computer science, software engineering, information technology graduate, or similar. Or if you don’t just go ahead and submit your CV too, we will suss you out
hmm...
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
07-21-2018 , 03:58 AM
Quote:
Originally Posted by RustyBrooks
Something I think you really need to tighten up is your understanding (or your expression of) big O notation. All the algorithms posted are the same in big O, O(n). From a computer science point of view they're all equivalent, pick the one that you can write correctly on the whiteboard.

Technically an O(n) solution takes
cn + k
where c and k are constants and different solutions will have different values of these. Some might work better for small n or for large n depending on the values of c and k, but all will outperform an O(n^2) or even O(nlogn) once n gets large enough, which is the point of big O.
Thanks for this btw. Fun exchange at my big 3 hour onsite today (which was like half technical grilling from different angles):

Interviewer: "What data structures do you like?"

Me: "Um hashtable?"

I: "Have you used trees?"

Me: "Um not really"

... (goes on like this) ...

Me: "I don't have a computer science degree, so..."

I: "Do you know about O notation?"

Me: "Oh sure of course duh"

I: "So if an array is n, what do you think a tree is?"

Me: "You mean to traverse it?"

I: "Yeah"

Me (looks up and thinks for a few seconds): "Log(n)?"

I: "Right"


For some reason n/2 didn't feel right, so I just guessed log(n) based 100% on your post. But it came off like I reasoned it out on the spot. BOOM
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
07-21-2018 , 04:03 AM


Dammit, Test Co already found their developer.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
07-21-2018 , 04:25 AM
Oh yeah, very interesting whiteboard problem from the same guy grilling me about big O:

Code:
// write a function that returns all arguments multiplied together 
// for n chained function calls

let result1 = mul(2).value; // 2
let result2 = mul(2)(3).value; // 6
let result3 = mul(2)(3)(4).value; // 24

// etc.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
07-21-2018 , 10:04 AM
Quote:
Originally Posted by suzzer99
For some reason n/2 didn't feel right, so I just guessed log(n) based 100% on your post. But it came off like I reasoned it out on the spot. BOOM
Yeah, O(n/2) just isn't a thing - that's just O(n). So is n/100 or n/1e6 or n/1^100 and so forth. So is 10n or 1000n or 10000000000n. All O(n)

I know you don't have a CS background but interviewers who ask about O(n) are really going to harp on this. When you do an algorithm on a whiteboard and someone asks "can you do better" they don't mean "can you make itwice as fast" they mean "can you take it from n^2 to nlogn or n"


As a digression I remember when I was taking discrete math , which was all about combinatorics, graphs, trees, etc, some students were complaining about what was this "for" what purpose or use did it have, and the teacher, who I really liked a lot, got very frustrated. He came back to the next class and said, we're going to write a program to solve this problem. I don't remember exactly what the problem was but it involved counting how many numbers between 1 and n had some specific property.

Together we wrote a program and then he said, can anyone tell me what the performance of this program is and someone said "O(n)" and he was like close but no and then someone figured out that it was O(n logn) because as the numbers got bigger you had to examine more digits. How many digits does the number "n" have? Approximately log10(n). So we're all feeling pretty proud of ourselves.

Then the teacher busts out: you know we could actually just calculate how many numbers have, like, property A and how many have property B and then calculate how many have both. And he writes down a little equation for it and says "O(1). That's what this class is for"
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
07-21-2018 , 10:07 AM
As an addendum, I fully admit that I am usually the guy who reaches for the O(n logn) program instead of finding the equation. If you go to the probability forum you'll find that some people (me) will write a little simulation or enumerator to answer some questions, and some people will bust out 5 line long equations.

The equation wins in the long term if you want the same kind of answer to different problems, but I could usually get an answer sooner with a program to a one-off question, and I was usually pretty sure I was right.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
07-21-2018 , 10:29 AM
I like TDD when I have a really well defined and fairly isolated problem with some tricky edge cases.


I like it much less for more general development because I find I waste too much time on stupid tests or on tests that end up needing to completely change. I worked with 100% TDD consultants on one project and doing simple things like “add this field to this web page” took twice as long and you just wrote the same code twice - when just seeing the field on the ui was a pretty good sign that you hadn’t messed anything up.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
07-21-2018 , 10:35 AM
I think asymptotic stuff is something you can’t really just “pick up” without a strong educational background in it. I think you should just try to focus on common solutions to problems and maybe try to reason why they might be linear, logarithmic, etc. It's something that definitely didn’t come naturally to me for a long time but once it clicked, it clicked.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
07-21-2018 , 10:39 AM
Quote:
Originally Posted by RustyBrooks
As an addendum, I fully admit that I am usually the guy who reaches for the O(n logn) program instead of finding the equation. If you go to the probability forum you'll find that some people (me) will write a little simulation or enumerator to answer some questions, and some people will bust out 5 line long equations.

The equation wins in the long term if you want the same kind of answer to different problems, but I could usually get an answer sooner with a program to a one-off question, and I was usually pretty sure I was right.


I might have mentioned this before but I remember reading a blog post I really liked* about this guy who had a bunch of young friends who all worked on motorcycles. Whenever they had a problem they would just tinker with **** and try to get it to work. They also had an older guy that they’d go to for help who would always stop, think about the problem, test specific things, and reason out the actual problem that needed fixing.

The young guys were sometimes faster but sometimes couldn’t figure stuff out. The old guy was much more consistent.

I always liked the analogy and think it applies to lots of things. So I try to think about what approach I want to use whenever I’m facing a problem. I personally usually fall back on tinker and try **** but find switching to the methodical approach after a bit helps me avoid getting too frustrated and kind of gives me a fresh start on a hard problem.


* Although it was so long ago I’ve probably messed up all the actual details.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
07-21-2018 , 10:42 AM
I’ve always been torn on interviewing about big O. I personally don’t care too much about the theory but think it’s important people know how to evaluate how efficient their code is. And I’m not sure if I’ve met that many people that can do that without at least some grounding in the theory.

Although I don’t think memorizing various advanced data structures and their efficiency is that important since it can be looked up. So knowing something like tree efficiency isn’t that important to me.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
07-21-2018 , 10:44 AM
Quote:
Originally Posted by jmakin
all the bosses took the day off and the 2 main toxic people were gone so it was like straight up workaholics at the office today, started drinking at 1 and everyone played video games in the conference room LOL

Amazing what an effect a few crappy people can have on an office morale.


Sounds like the toxic people increase efficiency!

Spoiler:
kidding
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
07-21-2018 , 10:45 AM
Quote:
Originally Posted by suzzer99
Oh yeah, very interesting whiteboard problem from the same guy grilling me about big O:

Code:
// write a function that returns all arguments multiplied together 
// for n chained function calls

let result1 = mul(2).value; // 2
let result2 = mul(2)(3).value; // 6
let result3 = mul(2)(3)(4).value; // 24

// etc.
I think when you first posted it you didn't have the .value on the end, and I thought it wasn't possible with that syntax in most languages, but you could do mul(2)().

With the .value this works:

Code:
function mult(factor) {
  const m = (f) => mult(factor * f);
  m.value = factor;
  return m;
}

console.log(mult(2)(3)(4)(8).value);
Or the other way:

Code:
function mult(factor) {
  return (f) => f === undefined ? factor : mult(factor * f);
}

console.log(mult(2)(3)());
function composition seemed like magic to me when I first saw it, but I think when you get used to it this problem is straightforward.

Last edited by well named; 07-21-2018 at 10:49 AM. Reason: simplification
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
07-21-2018 , 11:42 AM
I write a lot of tests, but there are some real problems with true TDD.

I'm working on a project where I'm providing an API for another project at work. The project code bases are completely separate. We hashed out the schema for the API and we both got to work implementing "our side" with the goal to meet in the middle soon after. If you've ever done this, you know what happened. We missed things when designing the API schema and needed to make changes to it.

The problem is assuming you know what it means for your code to pass a test before you write the test, that you know what the "correct" output is. This is true for stuff like the "find the first matching character" examples above or the mult() thing, simple well defined functions. You should write tests for these first.

But for something with more complex interactions I think it's a good idea to actually get something that approximately does what you want, that you've gone through a few iterations on, before you start writing tests.

It takes me easily 2 or 3x as long to write a very simple test for something as it does to write the function. The less times I have to rewrite the tests, the better.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
07-21-2018 , 11:43 AM
If you write a good test you shouldn’t expect a certain output, you should expect a certain behavior - i think there’s a distinction.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
07-21-2018 , 11:48 AM
But out way of measuring behaviour is generally (always?) measuring output (meant in a general sense of a set and specific data state). Off the top of my head I don’t actually know a practical difference when writing tests.

So if I know I want a certain behaviour but don’t know the outputs along the way that will give it to me, like Rusty said, TDD isn’t particularly useful.

For example I was recently trying to do something with a dataset I’d never worked with before. I didn’t know the actual structure of the data or the limitations of how it’s stored and so it’s impossible for me to write good tests ahead of time. Once I’ve gone through a few iterations it should be clearer what I want tests for.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
07-21-2018 , 11:58 AM
Quote:
Originally Posted by jmakin
If you write a good test you shouldn’t expect a certain output, you should expect a certain behavior - i think there’s a distinction.
For many APIs, the output is the behavior. If you want return a list of users, whats the behavior, aside from that the output includes all the users it should, in the right order, with the right fields. What is that, aside from the output?

When you decide it should return a dict instead of a list, or it the fields should all be renamed, now you need to fix all the checks your test does. And let's be fair - you're not going to write the test again from scratch, you're probably going to look at your test failures and make the test match the new output, as long as you eyeball it and it "looks ok"

I have seen a lot of tests that are wrong (or that don't really test anything) that were clearly generated by a lot of test churn, i.e. changing the test 9 times because the functions being tested changed.

Another major problem is that good tests are usually doing dependency injection or mocking. As your code matures, your dependencies change, and you find yourself having to redo your mocks. It gets very messy.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
07-21-2018 , 12:04 PM
Yeah, it’s a good point about the mocks. When writing a self contained function TDD is generally fine because it’s easy to set all the state you need. If you’re working with various services and data stores it becomes WAY worse.

And I’m sure there are people that are really good at writing testable code who don’t have some of the same issues. But they’re super rare.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
07-21-2018 , 12:06 PM
Quote:
Originally Posted by RustyBrooks
Another major problem is that good tests are usually doing dependency injection or mocking. As your code matures, your dependencies change, and you find yourself having to redo your mocks. It gets very messy.
I get what you're saying but I think this is also helpful, in the sense that I often change the code, and the tests failing remind me to think carefully about the implications of a change, often in areas that were a little bit tangential. I tend to think the biggest benefit of good test coverage is actually in refactoring, rather than in initial functional QA, although it's good for both.

Doing it well definitely requires doing more than just modifying the tests to pass without thinking about what they are testing. so of course we get it wrong about as often as we get the code wrong. Even so it seems to me the odds that I get something wrong increase with the length of time since I last messed with that code, so if there's good existing test coverage it's usually helpful, even when the problem is just that the test is now out of date.

I agree though that I rarely write tests for new code before writing a certain amount of actual code. I can see why -- psychologically - there is supposed to be value to TDD, i.e. by writing the tests first you're not as primed to only test the happy path through the code that you had mind when you wrote it. But practically it seems like the difficulties already mentioned are usually larger than that benefit, at least IME.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
07-21-2018 , 12:19 PM
I think test coverage is really critical. I just think you can get version 0.9 written before you start the tests. You have to be disciplined, because otherwise you get railroaded into getting 1.0 out the door and 2.0 out the door and so forth with no tests.

I think any deployed code should have tests written and fairly good coverage. But like me and this other guy are going to go through dozens of blind deploys before we get something that is ready for prod. (By blind deploys I mean the code exists but is either feature flagged, or set to only work in CI, or essentially not "hooked" to anything, i.e. you have to know it's there and deliberately call it.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
07-21-2018 , 12:31 PM
Do you think test coverage needs to be as high on a typed language as a dynamic? In my experience there is almost always a ton less tests in a java project than a node or ruby
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
07-21-2018 , 12:56 PM
Typings make it easier to be certain that you haven't introduced a particular kind of bug by refactoring but I don't think that's enough to mean that you generally need less test coverage in java than in ruby for code accomplishing the same tasks. I haven't written as much code professionally in java but I wrote about the same amount of tests for the code I have written as I expect I would have in another language.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
07-21-2018 , 01:25 PM
I’ve worked with the same codebase for about 7 years now and it has both a python and java component. Our python app definitely needs more test coverage because even trivial methods benefit from a test that checks there are no typos or obvious errors that might only fail at runtime in production.

There are probably tools that help with this, but I suspect it’s still generally true.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
07-21-2018 , 01:39 PM
I think the typo problem is why (well, is one reason) a lot of people use linters with languages like python or javascript.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
07-21-2018 , 01:53 PM
Quote:
Originally Posted by well named
I think when you first posted it you didn't have the .value on the end, and I thought it wasn't possible with that syntax in most languages, but you could do mul(2)().

With the .value this works:

Code:
function mult(factor) {
  const m = (f) => mult(factor * f);
  m.value = factor;
  return m;
}

console.log(mult(2)(3)(4)(8).value);
Or the other way:

Code:
function mult(factor) {
  return (f) => f === undefined ? factor : mult(factor * f);
}

console.log(mult(2)(3)());
function composition seemed like magic to me when I first saw it, but I think when you get used to it this problem is straightforward.
You don't even have to create a new function.

Code:
function mul(input) {
   mul.value = input * (mul.value || 1);
   return mul;
}

document.write(mul(2)(3)(4).value); // 24
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote

      
m