Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
The next time you need to develop a new algorithm, ask Ruby for what you want, dont tell it what to do. Dont think of your code as a series of instructions for the computer to follow. Instead, ask Ruby for what you need !our code should state the solution to your problem, even if youre not sure what that solution is yet" Then dive into more and more detail, filling in your solutions gaps as you do. This can lead to a more expressive, functional solution that you might not find otherwise. Too often over the years Ive written code that consists of instructions for the computer to follow. Do this, do that, and then finish by doing this third thing. #s I write code I imagine I am the computer, in a way, asking myself $hat do I need to do first to solve this problem% $hen I decide, this becomes the first line of code in my program. Then I continue, writing each line of code as another instruction for the computer to follow. &ut what does '#sk, Dont Tell( mean exactly% #nd how could Ruby possibly know the answer when I ask it something% #n example will help you understand what I mean.
3ere 2ile4readlines saves all the lines of text into an array, which the parse method will process, returning the result in another array. )ater I 5oin the result lines together and print them out. 3ow do I implement parse% #gain, I imagine that I am the computer, that I am Ruby. 3ow do I find the lines that follow glimmer% $ell, obviously I need to loop through the array looking for the target word.
7nce I find the word, Ill start saving the lines into a new array called result. 8ince I want to save all the following lines and not 5ust the matching line, Ill also use a boolean flag to keep track of whether Ive already seen the target.
$hats wrong with this code% -othing really. It works 5ust fine, and its even somewhat idiomatic Ruby. In the past, I would have probably considered this done and moved on. 3owever, I can do better than this. I can ask Ruby for what I want, instead of telling Ruby what to do.
-otice on the left I changed the label from 'Instructions ( to '$hat do I want%( This reflects my new way of thinking about the problem. -ow, what does 'appear after the target word( mean exactly% It means the lines that appear in the array after +and including, the line containing the target. #h< in other words, the lines9after method should return a subset or slice of the array. Rewriting the problem in a different way lead me towards a solution I hadnt thought of before. -ow I can rewrite the '$hat do I want%( text like this
I rewrote what I want from Ruby to be more specific I want a 'portion of the array( and I want the portion 'including and following the line containing the target.( I havent written much code yet, but Ive taken a big step forward in how I think about the problem. 7n the right, Ive written code to return a subset of the array, lines=target9index...1>. &ut my solution is still incomplete/ what should target9index be% Thinking about this a bit, its easy to see how to find the line containing the target string I can use detect to find the line that includes the target word.
&ut Im still not done. I need the index of the line containing the target, not the line itself. 3ow can I find target9index% #gain, I shouldnt tell Ruby what to do +maybe create a local
variable and loop through the lines checking each one,. Instead, I should ask Ruby for what I need. $hat do I need% I need the index which corresponds to the line containing the target. In other words, I need to find +to detect, the target index, not the target line. 3eres how to do it
3ere I use Rubys detect method to search a range of index values, not lines. Inside the block I check whether the line corresponding to each index +lines=i>, contains the target. #t the bottom I return the correct slice of the array if I found the target, or an empty array if I didnt.
2irst of all, I have simpler, more terse code. )ess code is better. The lines9after method contains 5ust ? lines of code while the parse method contains A. 7f course, I could find ways to rewrite parse to use fewer lines, but any way you look at it lines9after is simpler than parse. The parse method contains two local variables which are changed, or mutated, by code inside the loop. This makes the method harder to understand. $hat is the value of flag% $hat about result% To really understand how parse works you almost need to simulate the loop inside your head, thinking about how the flag and result values change over time.
The lines9after method also contains two local variables. 3owever, they arent used in the same way C they arent changed as the program runs. The block parameter, i, while different each time the block is called, doesnt change inside the block. Its meaning is clear and unambiguous while that block is running. 8imilarly, the target9index variable is set once to an intermediate value, not changed once each time around a loop. Terse, simple code that doesnt change values while it is running is the hallmark of functional programming languages like 3askell or Dlo5ure. $hile these languages allow you to write concurrent code without using locks, their chief benefit is that they encourage +Dlo5ure, or even force you +3askell, to write simple, terse code. Dode that asks the computer for what you need, not code that tells the computer what to do. &ut, as weve seen, you dont need to abandon Ruby to write functional code. Update: 8imon ErFger and Gosh Dheek both suggested using drop9while, which gives us an even more readable, functional solution
I also decided to rename the after method to lines9after, based on the comments from TenderHlove and Gohn Eary. I agree with them after would make more sense if I called it as a method on an ob5ect containing the lines +e.g. lines.after,. &ut as a simple function like in this example lines9after is more expressive. Thanks guys"
methods. 8tepping back, it can also help us design ob5ect oriented applications that are easier to maintain and extend. Update #2: #pparently Ive +unknowingly, conflated '#sk, Dont Tell( with the 'Tell, Dont #sk,( advice Dave Thomas has been giving us for years to make a different but related point about ob5ect oriented design. Dave explains here Telling, #sking, and the :ower of Gargon. 3e also disagrees with my opinion that the parse9lines example was written in a functional style. :at 8haughnessy