www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - byLine(n)?

reply helxi <brucewayneshit gmail.com> writes:
I was writing a program that reads and prints the first nth lines 
to the stdout:

import std.stdio;

void main(string[] args)
{
     import std.algorithm, std.range;
     import std.conv;
     stdin.byLine.take(args[1].to!ulong).each!writeln;
}

As far as I understand the stdin.byLine.take(args[1].to!ulong) 
part reads all the lines written in stdin.
What if I want to make byLine read only and only first nth line?

stdin.byLine(args[1].to!ulong).each!writeln;

Obviously the code above won't work. Is there any efficient 
workaround?
Jun 10
next sibling parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Sunday, 11 June 2017 at 05:36:08 UTC, helxi wrote:
 I was writing a program that reads and prints the first nth 
 lines to the stdout:

 import std.stdio;

 void main(string[] args)
 {
     import std.algorithm, std.range;
     import std.conv;
     stdin.byLine.take(args[1].to!ulong).each!writeln;
 }

 As far as I understand the stdin.byLine.take(args[1].to!ulong) 
 part reads all the lines written in stdin.
 What if I want to make byLine read only and only first nth line?

 stdin.byLine(args[1].to!ulong).each!writeln;

 Obviously the code above won't work. Is there any efficient 
 workaround?
You need only the nth line? Then you'd need to `drop` the preceding ones: void main(string[] args) { import std.algorithm, std.range, std.stdio, std.conv; stdin.byLine.drop(args[1].to!int-1).front.writeln; } Or if you need every nth line, combine `drop` and `stride`: void main(string[] args) { import std.algorithm, std.range, std.stdio, std.conv; auto step = args[1].to!int; stdin.byLine.drop(step-1).stride(step).each!writeln; }
Jun 10
next sibling parent Jonathan M Davis via Digitalmars-d-learn writes:
On Sunday, June 11, 2017 06:28:18 Stanislav Blinov via Digitalmars-d-learn 
wrote:
 On Sunday, 11 June 2017 at 05:36:08 UTC, helxi wrote:
 I was writing a program that reads and prints the first nth
 lines to the stdout:

 import std.stdio;

 void main(string[] args)
 {

     import std.algorithm, std.range;
     import std.conv;
     stdin.byLine.take(args[1].to!ulong).each!writeln;

 }

 As far as I understand the stdin.byLine.take(args[1].to!ulong)
 part reads all the lines written in stdin.
 What if I want to make byLine read only and only first nth line?

 stdin.byLine(args[1].to!ulong).each!writeln;

 Obviously the code above won't work. Is there any efficient
 workaround?
You need only the nth line? Then you'd need to `drop` the preceding ones: void main(string[] args) { import std.algorithm, std.range, std.stdio, std.conv; stdin.byLine.drop(args[1].to!int-1).front.writeln; } Or if you need every nth line, combine `drop` and `stride`: void main(string[] args) { import std.algorithm, std.range, std.stdio, std.conv; auto step = args[1].to!int; stdin.byLine.drop(step-1).stride(step).each!writeln; }
Another thing to note is that byLine reuses its buffer. So, every time popFront is called on it, the contents of the buffer are replaced. So, if any code keeps that dynamic array around without (i)duping it, then you get buggy code. So, whether byLine's reuse of the buffer is a nice efficiency boost (since it avoids reallocating the buffer) or a bug waiting to happen depends on what your code is doing. I think that there's a decent chance that byLine will work properly in this case, but I don't know for sure. If it _is_ a problem that byLine reuses its buffer, then use byLineCopy instead. Personally, I'd be leery of using byLine outside of foreach loops because of the buffer reuse, but some range-based code _can_ use it correctly. You just need to be aware of the issue so that you switch to byLineCopy or (i)dup the buffers manually if byLine's behavior is not what's correct for your code. - Jonathan M Davis
Jun 11
prev sibling parent reply helxi <brucewayneshit gmail.com> writes:
On Sunday, 11 June 2017 at 06:28:18 UTC, Stanislav Blinov wrote:
 On Sunday, 11 June 2017 at 05:36:08 UTC, helxi wrote:
 I was writing a program that reads and prints the first nth 
 lines to the stdout:

 import std.stdio;

 void main(string[] args)
 {
     import std.algorithm, std.range;
     import std.conv;
     stdin.byLine.take(args[1].to!ulong).each!writeln;
 }

 As far as I understand the stdin.byLine.take(args[1].to!ulong) 
 part reads all the lines written in stdin.
 What if I want to make byLine read only and only first nth 
 line?

 stdin.byLine(args[1].to!ulong).each!writeln;

 Obviously the code above won't work. Is there any efficient 
 workaround?
You need only the nth line? Then you'd need to `drop` the preceding ones: void main(string[] args) { import std.algorithm, std.range, std.stdio, std.conv; stdin.byLine.drop(args[1].to!int-1).front.writeln; } Or if you need every nth line, combine `drop` and `stride`: void main(string[] args) { import std.algorithm, std.range, std.stdio, std.conv; auto step = args[1].to!int; stdin.byLine.drop(step-1).stride(step).each!writeln; }
I was actually just looking for ways to read the first n line and then print it ($man head). My current program 1. reads all the lines provided by the stdin (bottleneck) 2. takes the first n lines (bottleneck) 3. prints each line I want to 1. read all the lines 2. when line number n is reached, stop reading the rest of the input 3. print each line
Jun 11
parent reply Cym13 <cpicard openmailbox.org> writes:
On Sunday, 11 June 2017 at 12:44:05 UTC, helxi wrote:
 On Sunday, 11 June 2017 at 06:28:18 UTC, Stanislav Blinov wrote:
 On Sunday, 11 June 2017 at 05:36:08 UTC, helxi wrote:
 [...]
You need only the nth line? Then you'd need to `drop` the preceding ones: void main(string[] args) { import std.algorithm, std.range, std.stdio, std.conv; stdin.byLine.drop(args[1].to!int-1).front.writeln; } Or if you need every nth line, combine `drop` and `stride`: void main(string[] args) { import std.algorithm, std.range, std.stdio, std.conv; auto step = args[1].to!int; stdin.byLine.drop(step-1).stride(step).each!writeln; }
I was actually just looking for ways to read the first n line and then print it ($man head). My current program 1. reads all the lines provided by the stdin (bottleneck) 2. takes the first n lines (bottleneck) 3. prints each line I want to 1. read all the lines 2. when line number n is reached, stop reading the rest of the input 3. print each line
byLine doesn't reall all input at once. Using byline and take you are effectively reading only the right amount of lines and not reading the rest. You already have what you want, what makes you think the contrary?
Jun 11
parent reply helxi <brucewayneshit gmail.com> writes:
On Sunday, 11 June 2017 at 12:49:51 UTC, Cym13 wrote:
print each line
 byLine doesn't reall all input at once. Using byline and take 
 you are effectively reading only the right amount of lines and 
 not reading the rest. You already have what you want, what 
 makes you think the contrary?
Oh it was my lack of understanding then. Thank you. I would also be really humbled if you demonstrate a faster approach of achieving the goal of the program :) (without explicitly using loops and conditions)
Jun 11
parent ag0aep6g <anonymous example.com> writes:
On 06/11/2017 03:00 PM, helxi wrote:
 I would also be 
 really humbled if you demonstrate a faster approach of achieving the 
 goal of the program :) (without explicitly using loops and conditions)
Do you have a reason to believe that your version is slow? I don't see why it would be.
Jun 11
prev sibling parent reply Cym13 <cpicard openmailbox.org> writes:
On Sunday, 11 June 2017 at 05:36:08 UTC, helxi wrote:
 I was writing a program that reads and prints the first nth 
 lines to the stdout:

 import std.stdio;

 void main(string[] args)
 {
     import std.algorithm, std.range;
     import std.conv;
     stdin.byLine.take(args[1].to!ulong).each!writeln;
 }

 As far as I understand the stdin.byLine.take(args[1].to!ulong) 
 part reads all the lines written in stdin.
 What if I want to make byLine read only and only first nth line?

 stdin.byLine(args[1].to!ulong).each!writeln;

 Obviously the code above won't work. Is there any efficient 
 workaround?
Ok, if I read you right you are writing to stdin and want first to print the first args[1] lines, then to do other things with the other lines of stdin. If you use byLine you will not read all the lines of stdin, but you will lose a line. From there I see three possibilities: 1) If you control the input, add a limit line (if you want to take 2 then the third line will be lost): import std.conv; import std.stdio; import std.range; import std.algorithm; void main(string[] args) { auto limit = args.length > 1 ? args[1].to!ulong : 2; writefln("First %d lines", limit); stdin.byLineCopy.take(limit).each!writeln; writeln("Next lines"); stdin.byLineCopy.each!writeln; } 2) Read all stdin and separate those you want to print from the others later: import std.conv; import std.stdio; import std.range; import std.algorithm; void main(string[] args) { // I used byLineCopy because of the buffer reuse issue auto input = stdin.byLineCopy; auto limit = args.length > 1 ? args[1].to!ulong : 2; writefln("First %d lines", limit); input.take(limit).each!writeln; writeln("Next lines"); input.each!writeln; } 3) Do not use byLine for the first lines in order to control how much you read. import std.conv; import std.stdio; import std.range; import std.algorithm; void main(string[] args) { auto limit = args.length > 1 ? args[1].to!ulong : 2; writefln("First %d lines", limit); foreach (line ; 0 .. limit) { // I use write here because readln keeps the \n by default stdin.readln.write; } writeln("Next lines"); stdin.byLine.each!writeln; } There are other options but I think these are worth considering first.
Jun 11
parent Cym13 <cpicard openmailbox.org> writes:
On Sunday, 11 June 2017 at 08:33:16 UTC, Cym13 wrote:
 On Sunday, 11 June 2017 at 05:36:08 UTC, helxi wrote:
 [...]
Ok, if I read you right you are writing to stdin and want first to print the first args[1] lines, then to do other things with the other lines of stdin. [...]
Meh... I just noticed my first and second propositions are essentially the same, the main difference is that the range stdin.ByLineCopy is stored so you don't create a new one when reading from stdin again. This is what causes the loss of a line.
Jun 11