你有没有想过在“笔记本电脑”编码面试中时间都去哪儿了?这是您带上自己的小型笔记本电脑(或者,他们提供一台,我猜)的事情之一,并且您应该当场解决一些问题。他们可能会给你 60 到 90 分钟的时间来完成这项工作,他们希望看到它发挥作用,并进行测试,以及所有这些好东西。
面试官递给你一张纸,可能会也可能不会讨论。然后他们让你放松并消失一段时间。只有你和你的小机器,如果你提前考虑的话,也许还有一些音乐。
时间都去哪儿了?我最后一次经历它时,它在我的脑海里是这样的:
(看着纸)嗯,好吧,那么,他们在这里给我什么。我必须从 Dropbox 中获取一些文本文件。好吧,上他们的访客网络,去搜查他们的文件。两个输入文件,两个输出文件。我应该通过运行他们在纸的其余部分解释的一些算法来获取输入并将其转换为输出。
该文件是面向行的,因此第一行是一个数字,表示后面有多少数据行。然后他们有更多的行,每行都有两个数字,用空格分隔。这不是来自 DOS 机器,所以至少不是 CRLF。它不是来自老式 Mac,所以也不是单纯的 CR。一切都以换行结束,典型的 Unix 东西。
对,所以,我得想办法解决他们给我的实际问题,然后我得写。但是,我还需要实际吸入这个文件并让数据通过我最终编写的那个“引擎”运行所需的所有东西。喜悦。
所以我想我可以写一些可怕的东西来使用 fgets 来阅读它并给我一堆行,然后以某种非常糟糕的方式将其切碎,通过 strtoul 或其他东西将其捣碎以从中产生价值,然后可能用两个参数调用我的“引擎”。喜悦。
是的,所以,让我们继续进行基础级别的工作,并希望算法在我执行此操作时开始设置到位。我对如何处理它有一些想法,但需要尝试一下,看看它是如何进行的,直到我得到一些实际向它抛出数字的东西,我才能做到这一点。
那么,让我们开始吧。嗯,好吧,我这里没有任何个人图书馆的东西,所以我想我可以用老式的方式来编码。另外,他们将在什么基础上构建它?我不知道它们在编译器方面的位置。让我们假设 C++98 只是为了安全。
好的,需要获取文件,然后读取文件,所以…
int main(int argc, char** argv) { 如果(argc!= 2){ fprintf(stderr, "用法: %s <文件>\n", argv[0]); 退出(1); }
…当然这意味着我们需要 stdio.h 用于 fprintf 和 stdlib.h 用于退出,所以在顶部添加那些#includes。
继续。打开那个东西,如果它不存在就大喊大叫。我通常有自己的 LOG 内容,但这里没有。失败我通常会 fprintf 到 stderr,给它一条消息和一个格式字符串,然后将 strerror(errno) 塞进去,但时间很短。 perror 很糟糕,但它更快,所以拧它,使用它。
文件* f = fopen(argv[1], "r"); 如果(!f){ 错误(“打开”); 退出(1); }
现在我们也需要 errno.h。去#include那个。
是时候拿到第一行了。让我们只阅读它,完全忽略它可能会在此读取时出现 EOF 并且我们可能什么都得不到的事实,因为这里再次紧迫了时间。废话,这会很糟糕。
而且,呃,废话,fgets 在最后留下换行符,所以我必须把它绑起来。同样,我想我只是假设我在这里得到的不仅仅是空行,并假设我可以从 strlen 中减去 1 而不会进入杂草。这就是为什么我在家里有所有这些花哨的阅读文件到行的东西,这些东西都是这么辛苦写的!
字符缓冲区[1024]; fgets(buf, sizeof(buf), f); buf[strlen(buf) - 1] = '\0';
肮脏的。很脏。现在我们需要一个数字。 strtoul 将是正确的事情,然后是你应该做的所有错误检查,但这是大量的工作,而且我的家庭图书馆集没有一个可以拯救我(它已经完成了这项工作在里面)。去死吧,atoi,希望没有什么不适合的。
int input_lines = atoi(buf);
[ 时间检查:我已经花了 17 分钟*写这篇文章*才把这件事搞砸了。我将根据我的笔记和几年前那一天的代码。滴答,滴答,滴答。 ]
最好说出我出于调试目的得到了什么。使用 stderr 这样它就不会破坏输出。
fprintf(stderr, "XXX 输入行: %d\n", input_lines);
现在我想是时候开始一个循环来读取这些废话并将其拆分并将其存储在某个地方。所以,嗯…
地图<int, int> 输入作业; // key:开始时间,val:运行时间 而(fgets(buf,sizeof(buf),f)){ 如果 (strlen(buf) < 1) { 继续; } buf[strlen(buf) - 1] = '\0'; char* sp = strchr(buf, ' '); 如果(!sp){ fprintf(stderr, "坏行 (没有空格): %s\n", buf); 退出(1); } *sp = '\0'; char* len_str = sp + 1; int start_at = atoi(buf); int run_for = atoi(len_str); input_jobs[start_at] = run_for; } f关闭(f);
这里太糟糕了。必须在上面添加东西:#include <map> 并使用 std::map。我真的希望他们不要给我重复的开始时间,否则我会压扁前一个。 (我认为他们说过“这不会发生”之类的话,但你为什么会相信呢?)
再次盲目地假设我们有 atoi 的可用数据……以及没有空格的坏线的东西,嗯,这也不应该发生,但同样,我不太信任他们。我愿意经历一些愚蠢的事情,但是当它从 strchr 返回一个 NULL 并稍后尝试取消引用它时,这会做可怕的事情,所以与其编写另一个可怕的 80 年代 C 指针漏洞,不如让我们死吧。
哦,是的,我想我应该确保他们给了我他们告诉我他们会给我的东西,因为我正在阅读 EOF。
回到循环上方…
int 实际 = 0;
…在循环中…
++实际;
…在循环之后:
如果(实际!= input_lines){ fprintf(stderr, "预期 %d 得到 %d\n", input_lines, 实际); 退出(1); }
是的,所以现在我至少会知道这是否出于某种原因。
[时间检查:写这篇文章已经过去了 27 分钟。 ]
现在我想我可以遍历我读取的数据并用它做一些有用的事情。哦,是的,通过首先阅读它而不是“流式传输”它,我基本上已经承诺自己充其量是 O(n) 内存情况,它可能最终会比这更糟。所以这个临时的“读入地图”的事情可能不得不在某个时候进行,我将从 for 循环内部调用“添加我刚刚解析的内容”。喜悦。
反正。迭代。但是,好吧,C++98,如此老派的迭代,没有“for const auto”的东西。
地图<int, int>::const_iterator ii; int job_count = 0; for (ii = input_jobs.begin(); ii != input_jobs.end(); ++ii) { const int& start_time = ii->first; const int& run_time = ii->second; add_job(开始,时间,run_time,job_count++); }
显然现在我们需要在 main 之上的东西来完成这个 add_job,所以让我们写一个存根来开始它。
静态无效添加作业(int start_time_raw,int run_time,int job_number){ fprintf(stderr, "XXX add_job %d | %d | %d\n\n", start_time_raw、run_time、job_number); // XXX给我写信 }
好的。我现在要停在那里。意识到您所看到的是该代码的*最终形式*。中间发生了很多事情。例如,看看 add_job 函数中如何调用“start_time_raw”?那是因为文件中的开始时间值原来是“HHMM”。也就是说,中午 12:00 在文件中为“1200”。 1:45 PM 在文件中为“1345”。
我最初将它解析为 0-1439 范围内的东西,因为,好吧,给定一个系统,它可以让您在一天中的给定时间以一分钟的精度安排事情,就个人而言,我将使用“数量午夜后的分钟数”作为基础。他们不是。他们使用人类可读的时间,而不是我们为了可读性而放在中间的常用冒号!
我怎么知道1439?我以前写过调度程序,一个没有任何 DST 转换的普通民用日是 1440 分钟长:24 * 60。我也从编写这些相同的调度程序中知道,那些相同的非 DST 天的一周是10080 分钟,因为那只是 24 * 60 * 7,而且你经常遇到这个数字。它会在你的脑海里烤熟。或者至少,它被烤进了*我的*脑袋。我能说什么,我就是这么奇怪。
所以,它是 start_time_raw 因为它没有被拆分并变成一个在数组中有意义的值。最终 add_job() 中的第一段代码最终做了一些可怕的事情,将 HHMM 更改为 0-1439 范围内的“自午夜以来的分钟数”,如下所示:
int start_hour = start_time_raw / 100; int start_minute = start_time_raw % 100; int start_time = start_hour * 60 + start_minute;
太可怕了,对吧?没有完整性检查。它只是修改、除法、乘法、加法和关闭。
现在,在这一点上,我还没有为实际研究算法做任何该死的事情,所以我什至没有在这里谈论它,因为我想指出要花多少时间才能打好基础让你对数据做任何事情。
获取文件名,打开文件,读取内容,将它们从文本转换为数字,将它们放在合理的地方,然后开始迭代它们需要一些时间。如果你在坐下来开始打字时确切地知道你需要什么,你可能可以把它撕下来,我不知道,也许 15-20 分钟?
但是,你能不能在面试中做到这一点,远离你通常的设置,在笔记本电脑键盘上,在笔记本电脑屏幕上,在一个有趣的椅子上,在一张奇怪的桌子上,时钟在滴答作响,并且有人(理论上)密切关注你?此外,您有可能无法获得这份工作。你可能需要这份工作来赚钱来维持你的房子或公寓,养活你和你所爱的人,以及所有这些好东西。
但是,嘿,没有压力。
我经历了这一切,我没有任何风险。如果我没有得到这份工作,那就太重要了。我不需要这份工作。我也比你的普通应届毕业生大得多,并且已经厌倦了不把太多的自我价值放在这些面试官对我或我的代码的看法上。
此外,我已经用 C 语言完成了整个“argc, argv, fgets into buf, squash the newline, do it in a loop, 把它切到一个空格上并处理结果” 在我的生命中可能超过一百次至此。我知道所有的部分。我也知道哪些部分会出错。为了节省时间,我必须决定要跳过哪些内容以及保留哪些内容以进行完整性检查。我不必花时间尝试完成这项工作。没有走上这条路的人不会那么容易。
但是……即便如此,我还是像疯了一样大汗淋漓,当它结束时,我感觉很糟糕……尽管,再一次,没有什么可以骑在这上面。如果我没有得到这份工作,那么我就不会和一些希望我去那里并邀请我去那里面试的朋友成为同事。我会回家继续写关于这个行业的讽刺故事,就像这里一样。
现在想象一下,你 22 岁,你需要这份工作,以及所有这些东西。你有实际的事情处于危险之中。想象一下你的感受,以及在这种压力下,这会对你解决问题的能力产生什么影响。
顺便说一句,当遇到这个问题时,我终于开始研究“引擎”和算法,但我选择了错误的实现。就我想象解决方案的工作方式而言,我最终把注意力集中在了错误的“轴”上,并陷入了死胡同。具有讽刺意味的是,我以前做一个调度程序(为人们)的经验让我试图用它作为解决方案,但这并没有达到他们想要的效果。
不是在断开与问题的连接并花一些时间远离计算机之后,它才来到我身边,我想通了。
那是什么?您没有时间断开连接然后再回来,因为您正在计时,而且他们很快就会来检查您?太糟糕了!祝你好运,找出你出错的地方。
那。那就是时间流逝的地方。
…
先发制人的侧边栏:“她应该用过 Python”。刚刚离开。你*so* 错过了重点。