Skip to content

Conversation

@udithprabhu
Copy link
Contributor

Added the necessary test function
Added example go program

@bitfield
Copy link
Owner

Thanks for the PR! Don't forget to update the README, and add the new method to the stress-test list, as described in the contributor's guide checklist.

filters.go Outdated
return Echo(output.String())
}

// Last reads from the pipe, and returns a new pipe containing only the last N
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the methods in this file are in alphabetical order, per the contributor's guide. It just makes it easier to find stuff.

filters.go Outdated
return p
}
scanner := bufio.NewScanner(p.Reader)
var stringArr []string
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Go we don't tend to name our variables after their type (as someone said, you don't name your pets "Cat" and "Dog"). Sure, this is a slice of strings (not an array), but what's it for? The name should clarify the contents; the compiler can already tell us the type.

filters.go Outdated
scanner := bufio.NewScanner(p.Reader)
var stringArr []string
for scanner.Scan(){
stringArr = append(stringArr, scanner.Text())
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with this is that we read the whole pipe into memory, before throwing away most of it. Would it be more efficient to use something like a ring buffer that just stores the last X lines?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't think of a more efficient way than to read the whole pipe and the current implementation exposes a Reader, and the Reader interface does not have functions to find the length of the content or going to an arbitrary token index.
That is why to find the last n tokens, we need the number of tokens and for that, we need to read the whole Pipe.

Please let me know if I am wrong or if there is a more efficient method to read the content.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, to take a trivial example, suppose we just want the very last line. We could do something like the following:

  1. Read a line from the input
  2. If not the end of input, goto 1.
  3. Return the line

The question is really how many lines we need to store in memory, given that we want the last X lines. The logical answer is just X, isn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, will try to incorporate this logic also

filters.go Outdated
}

if len(stringArr) == 0 {
return Echo("")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want an empty pipe, it's simpler to use NewPipe().

filters_test.go Outdated
if err != nil {
t.Fatal(err)
}
input := File("testdata/last.input.txt")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this file is identical to first.input.txt I suggest we just re-use that file instead.

filters_test.go Outdated
t.Error("input not closed after reading")
}
input = File("testdata/last.input.txt")
gotZero, err := input.First(0).CountLines()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be Last(0), not First(0).

filters_test.go Outdated
if err != nil {
t.Fatal(err)
}
got, err = File("testdata/last.input.txt").First(100).Bytes()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto.

@udithprabhu
Copy link
Contributor Author

Thanks for the review, will send the updated pr in a day or two

// Last reads from the pipe, and returns a new pipe containing only the last N
// lines. If there is an error reading the pipe, the pipe's error status is also
// set.
func (p *Pipe) Last(lines int) *Pipe {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have changed the implementation according to your suggestion. Please review

Copy link
Owner

@bitfield bitfield left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking really good! Excellent examples. Just a few little tweaks and then I think this will be ready to merge.

README.md Outdated
* [head](examples/head/main.go)
* [echo](examples/echo/main.go)
* [tail](examples/tail/main.go)
* [least frequent](examples/least_freq/main.go)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make the name least_freq to match the directory.

)

func main() {
lines, err := strconv.Atoi(os.Args[1])
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens when there are no command-line arguments?

filters.go Outdated
input := make([]string, 0, lines)
for scanner.Scan() {
if len(input) == lines {
input = append(input[1:], scanner.Text())
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like it might be a little inefficient; have you done any performance testing on it? I wonder if this might be a good place to use the standard library ring package?

filters.go Outdated
if p == nil || p.Error() != nil {
return p
}

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll notice that there are no blank lines within any functions or methods in the script package. I tend to think Go code is more readable the more of it you can see at once! For consistency of style, would you mind removing the blank lines?

@bitfield
Copy link
Owner

Also, don't forget to mention in the PR description, "Fixes #18" (or something like that). Then GitHub will automatically link it and close the issue when the PR is merged.

@bitfield
Copy link
Owner

@udithprabhu how's it going? I hope I haven't overwhelmed you with too many changes. If you like, I could pick up this PR and make the requested changes myself, then we can both assess the state of it and see whether it's ready to merge.

@udithprabhu
Copy link
Contributor Author

@bitfield , Sorry for the delay, boggled down by other work, will finish this within 2-3 days if that is fine with you?

@bitfield
Copy link
Owner

Not at all! Take all the time you need, your contribution is much appreciated. Just let me know if I can do anything to help.

@udithprabhu
Copy link
Contributor Author

@bitfield, I think I am more or less done with all the requested changes. please let me know if further tweaking is necessary.

@bitfield
Copy link
Owner

I'll take a look! Thanks very much!

@bitfield
Copy link
Owner

Excellent work! Really outstanding. Thanks very much for the contribution.

@bitfield bitfield merged commit 7010d13 into bitfield:master Aug 25, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants