|
Hi I'm an idiot, can someone tell me how in the hell you add a local library to a project in PyCharm? Explain it like half my brain is missing, because it feels like it is after the past hour (for some reason this is an amazing fertile ground for spam websites scraping Stack Exchange) I've installed the PyCrypto binaries since I don't have a C compiler installed, so that's living in Python34\Lib\site-packages right now. My project is set up with a VirtualEnv. I can install packages from the Project Interpreter screen, which is nice and all since they come from the internet, but I need to add this local package instead, and I can't see any way to do that. Closest I've got from randomly clicking on things (which is where I'm at now) is adding site-packages as a Content Root which at least makes it appear under External Libraries. Is this the right way to do things? Code completion isn't working at all, is that a separate issue or have I done this wrong?
|
# ¿ Dec 21, 2014 00:02 |
|
|
# ¿ Apr 27, 2024 09:08 |
|
Ok, I tried that one earlier but I think it failed because I added the Crypto folder and not the one above that. Is this basically the same as adding a Content Root? I'd test it out but now I've removed those paths it's still showing up in External Libraries. It sure is a mystery! e- scratch that, I've somehow got it showing Unresolved Reference errors on imports now when the code executes fine. If I change the import and drop the Crypto package prefix the inspection errors disappear, but the program fails because now it can't find the module baka kaba fucked around with this message at 00:45 on Dec 21, 2014 |
# ¿ Dec 21, 2014 00:34 |
|
Why would you want to do that though? Suspicious D's version basically has a set of clear fields you can use for whatever lookups you like, you're just smooshing them all into a string you'll have to take apart and analyse later. Naming complexity and efficiency aside, is this actually making your life any easier? Are you just uneasy about nesting dicts? Think of them as records in a table, if you like e- if you wanted a list of outdoor maps you could just do [map['map'] for map in maps if map['type'] == 'outdoors'] and you're done. I don't think it would be much different for a string, just slower and more prone to issues (like what if you want to tag several attributes with 'large' or 'small'? Do you prefix with the attribute so you know what 'small' in the string actually refers to?) baka kaba fucked around with this message at 02:36 on Jan 2, 2015 |
# ¿ Jan 2, 2015 02:27 |
|
Yeah your logic doesn't look right - try taking your first test case (19, 19, 3) and reading through the code to see where that set of inputs takes you. Then change it to (12, 12, 3) - which is completely ambiguous - and see where that leads you in the code path. It's a good idea to write more tests! Think of a few examples of all the awkward situations you'd need to test for (you've already been doing that in writing your code) and add them as tests, so you can be sure everything's working the way you think it is. And that way, when something fails you can debug it and find out exactly why it's tripping up Also you spelled 'ambiguous' wrong in your return statement, so it's technically not producing the required output for those cases
|
# ¿ Jan 4, 2015 12:47 |
|
MockingQuantum posted:Well yes, that's inevitable, but I guess I'm asking, as someone who is completely new to programming in general: Codecademy only gets you so far, so am I better off continuing with structured self-instruction, or try a project when I have only a loose understanding of how dicts and classes (for example) work in python? I get it if there's not a good answer that someone else can give me, but I'm trying to at least minimize frustration. You could have a look at CheckIO if you want to explore a bit further, they have a lot of structured tasks and challenges that go in lots of different directions, and generally leave you to find your own solution. Once you've written some code that solves the challenge, you can look at other people's solutions and pick up some neat tricks Having an idea of what you eventually want to do is always a good thing though, because at some point you need to jump in and get on with it. And it'll probably be bad at first, but that's all part of the learning process. You can learn to not be bad in general, pick up some useful tools and patterns, but specific projects have their own unique aspects you'll have to work out yourself, that you can't necessarily prepare yourself for. If you're stuck for project ideas though, that's a whole other problem!
|
# ¿ Jan 15, 2015 03:45 |
|
Here's a nice lambda overview http://www.secnetix.de/olli/Python/lambda_functions.hawk Also it doesn't really answer your general question, but There's A Library For That! https://docs.python.org/3/library/collections.html?highlight=counter#collections.Counter
|
# ¿ Jan 16, 2015 00:45 |
|
Yeah, it's silly huh? You might want to take a look at this too, if you have a bit of time: http://www.dabeaz.com/generators/Generators.pdf
|
# ¿ Jan 16, 2015 01:35 |
|
Fergus Mac Roich posted:edit: Wow, I think the answer might actually be in the itertools module you guys helpfully pointed out. The structure is basically like nesting for loops, so this: Python code:
Python code:
e- beaten, but also chain should work, but there are two versions of it itertools.chain(*iterables) Make an iterator that returns elements from the first iterable until it is exhausted, then proceeds to the next iterable, until all of the iterables are exhausted. Used for treating consecutive sequences as a single sequence. classmethod chain.from_iterable(iterable) Alternate constructor for chain(). Gets chained inputs from a single iterable argument that is evaluated lazily. (This is an amateur explanation coming up) This is a little tricky, but you see that asterisk in the first one? That unpacks a collection of arguments into in the individual elements. It turns 'hey chain, handle this box of stuff' into 'hey chain, handle this and this and also this'. The difference is that chain will treat the box (say your collection of iterables) as the -single- iterable you want it to operate on, and it will happily hand you each element inside it - but those elements are the things you actually wanted it to iterate over. So you need to unpack that collection as you hand it to chain, so it gets multiple elements instead of the container. If that makes sense! Like this: Python code:
baka kaba fucked around with this message at 04:21 on Jan 17, 2015 |
# ¿ Jan 17, 2015 04:13 |
|
Also y'know Counter() takes an iterable too - you can just stick your word generator in there, like Counter(words) and it will feed it and build up the totals itself. No need to increment things! You don't even need to create a variable, you can just make your generators and then do return Counter(words) . See the lines of code vanish before your eyes like dissipating steam! Well, back to Java for me
|
# ¿ Jan 17, 2015 05:17 |
|
Why not set a skip flag at C, and check it at A to see whether the stuff needs doing? Just make sure it's initially false so you process A the first time around (Which is the same basic idea as the above but with the logic in the loop itself, whichever makes most sense in your program's structure) baka kaba fucked around with this message at 05:30 on Feb 25, 2015 |
# ¿ Feb 25, 2015 05:27 |
|
caberham posted:Hey guys, I barely understand what's going on but I'm having a lot of fun learning Python and flask. Your logic is fine up until the 3rd one, where you suddenly change what you're doing (the numbers are slightly wrong too) Just to outline what's happening, integer division (//) divides by a number and ignores the remainder, while modulo ignores the division result and gives you the remainder instead. You can use both to kind of split values on a numerical boundary, and work out how much you have on each side, which is what you're doing here. #1 is easy, you're getting everything below 10 deciseconds. #2 is slightly more complex - you're getting everything below 100 deciseconds (i.e. the last two digits), then using integer division to reduce that to a single digit and ignore the remainder. Anything 100 or above is the business of the next digit, and anything below 10 is the business of the previous digit. So you're slicing and dicing to get the value that pertains to digit 2 (we're going from right to left) Hopefully you understand that since your algorithm already does that! You just need to apply the same logic to digit 3. If you look at your code you're actually dividing first this time. A value of 599 deciseconds should be returning 5 for this stage, but right off the bat you're dividing by 1000 and discarding the remainder, which gives you 0 before it even hits the modulo. You need to use modulo to remove the excess values and division to remove the lower ones. And 1000 is the wrong number anyway - maybe think of it like an analogue meter, what value ticks over and adds 1 to the next digit?
|
# ¿ Mar 10, 2015 08:51 |
|
caberham posted:
Well not really, the ones are base 10, that's why the tens are... well, tens. What stringent's doing there is getting the deciseconds for the first digit, then dividing by 10 to convert to seconds and doing modulo 60 to eliminate any full minutes, so you're only left with the remaining seconds. Then you can split that again into ones and tens for the separate digits You don't have to do it that way, you can just //10 to lop off the deciseconds and %10 to remove the tens and up, and you're left with the ones. And //100 will remove the decis and ones, %6 will remove anything above 5, and then you've got your tens digit You can do it any way that helps you keep track of what's happening (nothing wrong with treating two digits as a base 60 number if you want to), it's just useful to understand the general case and how you can use division and modulo to break off pieces and split numbers into whatever component parts or groupings you like baka kaba fucked around with this message at 10:09 on Mar 10, 2015 |
# ¿ Mar 10, 2015 10:04 |
|
You probably want to have a look at string formatting (If it's a bit much to look at, have a look at the examples, there's some using the exact same alignment trick as the method you're calling) Basically the output is tabulated, each temperature their method prints is padded to a fixed width, so the next one starts in the right place. Your issue is that your dates aren't padded, so the first temperature is printed too far to the left, and so all the rest are too. Look at their printing code and steal it!
|
# ¿ Mar 21, 2015 12:55 |
|
jimcunningham posted:No whitespace. Like i said, I didn't write that line I was having trouble shooting poo poo into the dict. got help on SO. What exactly are you trying to do? Like specifically, what are you working with and what do you need to do with it? You're processing a set of data here, so the first thing you need to do is work out what the data does or could look like. Then you need to work out how to pull out the separate bits of data, handling any possible variations and skipping junk lines, so each time you're starting fresh in the right place to read the next chunk of data. Once you have that sorted out, you just need to worry about handling the data you've pulled out - storing it or processing it, whatever. code:
Python code:
['>Rosalind_0498\n', 'ACTGCTGACTGACTGACTGACTG\n', '>Rosalind_2840\n', 'ATGCATGTTTACGACTACGTACTGCCGCGCGCC'] If that's the case, on each iteration you need to read in the first line, strip the newline and the leading text, and you have your header ID. Then you need to read in the second line and strip the newline. Once you have those clean pieces of data you can run your processing and wang them in a dictionary or whatever. So something like Python code:
But if you can put both bits of data on the same line, separated by a space, it makes things a little cleaner since you can just read a line at a time: Python code:
If this is enough really depends on your data - you might want to put checks in to make sure there aren't extra spaces, or underscores, or blank lines etc., things that will trip up the algorithm and require some error handling. Validation checking, basically. If it's a one-off script and you're sure your input file is ok then it might be enough. (Also you might want to make your dictionary keys ints when you add them?) Oh yeah, I've been a little bit wordy - you could definitely cut down the number of lines if you wanted (like doing the header split in the mapping line) but I wanted it to be clear what's happening. Your SO line looks like code golf, i.e. doing stuff in the minimum number of characters just for the hell of it, no wonder you're having trouble understanding it baka kaba fucked around with this message at 14:43 on Mar 27, 2015 |
# ¿ Mar 27, 2015 14:09 |
|
Well you can still do other things - identify the start of a new section by the > character starting a line, or check if a line only contains characters from GACT, and so on. Lots of options, the answer is always It Depends. Honestly the dicts are probably the easy part, there's a bunch of tricky stuff in there, so don't get discouraged The thing about this (and all programming) is that you need experience to be able to start from the very end and work backwards, like with your SO code. When you're solving a problem you need to be able to break it down into steps, and work out a system for handling those steps. When you're done and you have something that works, maybe then you'll want to slim it down and refine it, and maybe even make a one-liner. But you usually need to have gone through the experience of wrasslin' the problem, first. Otherwise you're just crossing your fingers and hoping, and when it doesn't work you don't know where to start looking
|
# ¿ Mar 27, 2015 16:34 |
|
Tried stepping through it with a debugger? That way you can watch it fill out entries and work out why it's tripping up on some
|
# ¿ Jul 5, 2015 12:41 |
|
Well I don't entirely know what's going on in there or much about these arrays, but aren't you skipping calculations for the first and last elements in each column? You're iterating over range(1, size-1), which starts on index 1 instead of 0, and stops before size-1 - so in a 5-element list you're handling indices 1, 2 and 3. You're accounting for that when you do a calculation, but you're not actually updating those edge elements I have no idea what I'm talking about so I might be completely misunderstanding what's happening, so maybe it's all cool. Learn to use the debugger! It's important - you might be hitting Step Over when you mean to do Step Into, or whatever the equivalents are, so learning how that works will help you a ton. It's so much faster than spamming logging in hopeful places
|
# ¿ Jul 6, 2015 00:47 |
|
That's after the first iteration of find_theta_and_nu2 - does that look right? Based on those values I get 0.11475 for row1 col2 as well
|
# ¿ Jul 6, 2015 02:01 |
|
Spaseman posted:That is EXACTLY what I needed, thank you so much. I've been fighting that problem for days and now I can finally move on. Do you understand why though? When you create a new object it's a unique instance, so you can't just check new_sword == old_sword because they refer to two different, individual objects, even though they're instances of the same class It's important because it comes up a lot, and you might have your own equality checking code so you can do simple comparisons (like keyring_1 comparing the list of doors it opens to the list that keyring_2 opens)
|
# ¿ Aug 4, 2015 16:23 |
|
I just dabble in Python so someone shout at me if I'm going wrong here... but as far as I'm aware, your scopes are basically dictionaries, so any attributes you use are effectively dictionary entries. Trying to access b.rubbed before it's been assigned is really looking up a key that isn't in the dictionary yet. So assigning your attributes in init means you're adding all the valid entries to your dictionary at the beginning, and anything that accesses them will get a value back. You can add them later, like how rub() assigns a rubbed entry, but then you have an issue with the dictionary's current state - if you try to get the value of that entry, has it actually been added yet? That can make it hard to reason about the actual state of the object, which is why it's easier to just create its attributes at construction time
|
# ¿ Aug 10, 2015 00:19 |
|
flosofl posted:PyCharm itself tracks a very tiny amount of changes, which has served me OK so far, but if I wander down a wrong path for too long, I realized I have no real recourse other than doing a "poor mans" VCS. i.e. Each time I do a change (other than fixing syntax) I put a version number on the file. This is getting out of control for me. PyCharm has Git integration so you get a nice UI and tools right in the IDE https://www.jetbrains.com/pycharm/help/using-git-integration.html It doesn't handle everything (I've had to crack open the command line to stop it tracking a previously tracked file) but for your general day-to-day version control it's great
|
# ¿ Sep 8, 2015 20:55 |
|
Gothmog1065 posted:Yeah, I already tried that and am getting an "out of range error." I had to leave work so I didn't poke it anymore at that time. I'll see if I can poke it more tomorrow. I'm not entirely sure what you're doing, but your first piece of code looks like it iterates over link_list, examines a bunch of 2-tuples, then sets disconnect to a 2-tuple. It resets this over and over until it finishes with the list, then once it leaves the loop, it calls link_list.remove() with whatever disconnect was last set to. Which should be whatever the result is for the last tuple in the list? You probably don't want to do that I'm guessing, and the remove call is meant to happen for each tuple in the list, after you've determined what to do with it The other thing I noticed is your second case checks if the tuple is (gateway, enemy), but then it sets disconnect to (enemy, gateway), which is a different tuple and won't match the remove call, if that's what's meant to be happening. Your last case sets disconnect to the last element of the list, so you're removing elements from the end (is that what you want?), and if you do it in the loop you'll be modifying the list you're iterating over, which isn't usually a good idea (you'd generally iterate over a copy and modify the original) Personally I'd stick a breakpoint on the problem line and run a debugger on it, see what the state of disconnect and link_list is when it falls over
|
# ¿ Sep 9, 2015 01:55 |
|
Veskit posted:Sorry, Your sum() function takes a list as a parameter - you 'pass it in'. That's what you're doing at the bottom in your main code - you create a list of numbers, and pass it into sum. Sum returns some result, which is what gets printed by the print() function (it gets passed in in the same way) So what does the sum() function actually do? Well, you defined it up at the top! You pass something in (you're calling it list), and then you iterate over each element in that sequence, adding 1 to your sum each time. Then you return that total as the result
|
# ¿ Sep 14, 2015 19:00 |
|
Veskit posted:BUT FOR SOME GOD FORSAKEN REASON THEY USED THE WORD SUM JUST TO CONFUSE THE EVER LIVING gently caress OUT OF ME Let me introduce you to some Java tutorials. Sum sum = new Sum()? You can think of it like this - there's a function called max() that takes two numbers* and returns the largest one. So wherever you call that function, it'll be replaced by the result when you run the program. This could be super useful if you want to find lots of largest numbers, or if it's a common task that's useful in all kinds of programs (which is why it's part fo the standard library, it's a useful utility). That's how you use it, but how does it actually work? Well, you define the function by giving it a name and listing some parameters it'll use, and then you write some code that takes those variables and does something with them, and comes up with an answer. A function is just a mini program, and it's a way of handling a task separately and neatly, so you can get the result you want without cluttering up your code with the implementation details, or reinventing the wheel. You don't have to use them, but they can be a nice organisational tool - and at some point you're going to find yourself retyping basically the same code at another point in your program. That's when it's a good idea to break that functionality out, and just say 'do thing with X and Y' *it can do more than two and they don't even have to be numbers! Another cool thing about functions, you can make them general and versatile, so you can just go 'hey give me the biggest thing', and the details of how that happens are squirreled elsewhere baka kaba fucked around with this message at 19:43 on Sep 14, 2015 |
# ¿ Sep 14, 2015 19:40 |
|
You could do something likePython code:
|
# ¿ Oct 25, 2015 00:12 |
|
I don't really use python that much, but my setup is Windows with PyCharm, and a bunch of venvs (not because I really need separate environments, just as a best practice thing). Working with the venvs feels like a pain, especially in a console - a lot of the time I prefer opening the whole IDE just to run a script. Last time I did a thing, I spent hours wrestling with the packaging system and ended up having to (I think) download a particular wheel and rename it to trick the system into installing it Anyway, am I an idiot for not using Anaconda? I don't really know a lot about it but it seems like y'all are using it, and if it removes any of the headaches I'm in
|
# ¿ Nov 5, 2015 11:00 |
|
SurgicalOntologist posted:Well, if you're having issues activating the environment, it won't help. I'm confused what you mean, though. In the console it's one line; in PyCharm you set it and forget it. (is there a shebang equivalent on Windows so you can hard-code the environment into the script?) I was more wondering if there was a way to streamline it really, so it's not "navigate to the relevant venv's scripts folder, activate it, navigate to the project folder, run the script", especially if you just want to bang it out as a quick command. It's fine within PyCharm obviously! Thermopyle posted:No, you're not an idiot. I switched from virtualenvs to anaconda because everyone in here is always talking about it, but for the use case of virtual environments, I don't really find it any better than virtualenv. I'll check out that wrapper, it looks a little better at least. And yeah my biggest headache was installing... lxml I think, I ended up researching the whole package management system and chasing down various binary versions and having to try faking the filename so it would install on my system... it was just a dependency for something else, I just wanted to import it and get on with what I was doing. So yeah, any extra layer of management convenience would be great I guess what I'm really asking is, is Anaconda like a 'default' Python distribution at this point? What the average person should be installing so they can get coding with a minimum of fuss and hassle?
|
# ¿ Nov 6, 2015 15:41 |
|
Yeah it's more the management stuff that sounds nice, I don't actually need all the packages - I mean being able to just make a library appear on your system when you need it is why package managers are neat! And I know Anaconda isn't the only one of these, I guess I was more asking if these systems are the 'default' over a straight Python download. Like how using some sort of IDE is the 'default' way to program as far as recommendations go, rather than just coding in notepad. That's probably not the best analogy but you know what I mean
|
# ¿ Nov 6, 2015 18:56 |
|
Yeah I really meant it in the "use this, idiot!" sense hence the FoiledAgain posted:PyCharm question: If you click the settings gear on that pane and you have an Autoscroll from Source option, it's that
|
# ¿ Nov 6, 2015 22:13 |
|
Thermopyle posted:You can also click the crosshairs/bullseye icon just to the left of the gear icon to scroll to your currently editing file in the Project view without turning on the AutoScroll thingy. Ohhh is that what that does. I poked it a few times and wondered LXML!!!
|
# ¿ Nov 7, 2015 00:35 |
|
Fergus Mac Roich posted:Yeah... it's still not intuitive to me why this algorithm can't do this(note that my script does model the constraint you mentioned) but I guess I should have read further. There are other internet resources making it clear that this algorithm is just inapplicable to longest path in a DAG, assuming that I'm right that this problem actually can be modeled in that way. I guess I'll come back to this problem one day when I understand graph algorithms better and maybe it'll make more sense. At least I have a solution. Thanks. I think your issue is something to do with this: From looking at this, it's obvious the longest path has to go through both 20s and that 8 Because of the way the numbers fall, that heavy 20 on the left hasn't had its distance set yet, because the 1 above it hasn't been visited. But we've already set both the distances of the other 20's neighbours, including the 8 at the bottom. Because they have the heaviest distances, they're going to get visited next and get removed from the unvisited group. There's barely anything left unvisited, and only now are we hitting that 20 The 20 checks its neighbours and adds its distance to them - the other 20 gets bumped up to 43 (in your implementation - Djikstra wouldn't do this, it only checks unvisited nodes, but it doesn't matter). What it really needs to do is propagate that to its neighbours, so the 8 can be updated to 51. But because the lower 20 has already been visited, it won't change its neighbours again. The last unvisited nodes are calculated, and the highest distance is 43 - which is wrong, and isn't from a node at the bottom of the graph either I think your issue is basically that your graph is one-way - you can only move downwards, which limits the ability of the algorithm to reach out to its adjoining nodes and keep a consistent smallest/biggest distance score. You only get one chance to visit a node, and once it's visited its distance can't be updated (in Djikstra's algorithm anyway - yours updates neighbours whether they're visited or not as far as I can see), but this pyramid has separate, distinct routes into each node since there's no backtracking, so each one needs to be evaluated. With Djikstra it's down to luck whether when you visit a node it has all its incoming route information in place, because you can't go upwards looking for it, if you get me If you're interested, this is how it plays out (I think...) without the 'downwards only' neighbour check, but still looking for the largest route instead of the smallest (red is going up, against the rules) So yeah, it looks like going for the heavier weight works fine, it's just the limitation on the direction you can go that makes it a bad fit (sorry for the megapost, I couldn't see anything wrong at first so I got curious)
|
# ¿ Nov 11, 2015 15:09 |
|
Works 4 me, did you see the error being thrown?
|
# ¿ Feb 1, 2016 14:19 |
|
Are you importing from 'bs4' instead of from 'BeautifulSoup'?
|
# ¿ Feb 6, 2016 01:58 |
|
The quotes are what make it a string
|
# ¿ Feb 13, 2016 18:43 |
|
you could do something likePython code:
What does the data look like anyway? Are the tab-delimited lines using spaces that you need to avoid splitting on?
|
# ¿ Mar 1, 2016 19:21 |
|
You could use integer division since it's one of the good uses for it!Python code:
Using non zero-based counting makes it more complicated since you have to translate to that and then back again. It would probably be neater to use an index starting at zero and add one to it when you're outputting readable numbers Python code:
|
# ¿ Mar 2, 2016 05:06 |
|
I always thought the idea was to make unused variables disappear into the background, so it makes the code's meaning easier to parse
|
# ¿ Apr 9, 2016 16:27 |
|
QuarkJets posted:The discussion was over the idea that _ is somehow superior to i in a list comprehension, which is preposterous. They're basically equivalent, and _ is a little obtuse if anything. Are they though? I mean I only dabble in python so this is more of an outsider's view (so it was a genuine question!), but to me if I see a variable called i I assume it's some kind of counter or index, with a value that's going to be used somewhere. Working out that it's missing from the rest of the code takes time, and involves more mental effort by keeping it in mind until you realise you should be ignoring it completely. Its presence kinda shapes the algorithm in your understanding, y'know? But _ just looks like a blank space, and when everyone always uses it to mean 'whatever' then it's a lot easier to understand and visually scan the code, for me anyway. I'd never seen it before Python (now I know it's in functional languages too), but I really like that it's there. It's just neat
|
# ¿ Apr 10, 2016 22:14 |
|
Well that's a pretty short comprehension so you're not saving a lot of brainpower, but the index isn't actually being used, right? It's there because it has to be there, but you're never using the value - it's just noise being produced by iterating over the length of the range To me using a blank like _ makes it immediately clear you're not using the actual values, and in a more complex bit of code it just cleans the whole thing up. It's like redacting the irrelevant bits. I mean sometimes it obviously doesn't really matter, but it feels like a more elegant solution to me than using something that looks like it could be important
|
# ¿ Apr 10, 2016 22:37 |
|
|
# ¿ Apr 27, 2024 09:08 |
|
The value isn't used though, is what I meant. You're really doing [[] for range(k)] except that's not valid, you have to assign a variable but you don't actually care about it yourself. If you did need to access it as an index later, then obviously that doesn't apply! That's what I mean, choosing whether or not to name it (with _ being 'unnamed') carries meaning and intent, and conveys something to the reader List comprehensions tend to be pretty compact anyway, right? Single-letter names can be more readable, especially if they fit the data well like x and y, so a convention where you use longer names for the stuff you care about seems clunky. With something like _ it's clear whether a variable is going to be used for something, or completely ignored. You don't even need to come up with a throwaway name for it! I mostly do Java though so maybe that's why I appreciate the brevity
|
# ¿ Apr 11, 2016 01:05 |