Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Indexable#find and #find! with start offsets #14642

Open
HertzDevil opened this issue May 29, 2024 · 4 comments
Open

Indexable#find and #find! with start offsets #14642

HertzDevil opened this issue May 29, 2024 · 4 comments
Assignees
Labels
good first issue This is an issue suited for newcomers to become aquianted with working on the codebase. kind:feature topic:stdlib:collection

Comments

@HertzDevil
Copy link
Contributor

Indexable has overloads of Enumerable#index and #index! that accept an additional optional starting offset argument:

module Enumerable(T)
  def index(& : T ->) : Int32?; end
  def index(obj) : Int32?; end
  def index!(& : T ->) : Int32; end
  def index!(obj) : Int32; end
end

module Indexable(T)
  def index(offset : Int = 0, & : T ->); end
  def index(object, offset : Int = 0); end
  def index!(offset : Int = 0, & : T ->); end
  def index!(obj, offset : Int = 0); end
end

The same is not true for Enumerable#find:

module Enumerable(T)
  def find(if_none = nil, & : T ->); end
  def find!(& : T ->) : T; end
end

module Indexable(T)
  # no overloads for `#find` and `#find!`
end

If x : Indexable then it seems natural that:

  • x.find!(offset, &block) should be equivalent to x.unsafe_fetch(x.index!(offset, &block));
  • x.find(if_none, offset, &block) should be equivalent to x.index(offset, &block).try { |i| x.unsafe_fetch(i) }.

So I think those additional overloads should be available in Indexable as well, so that we don't have to write the above unsafe_fetch (or even a regular #[] call).

@HertzDevil HertzDevil added kind:feature topic:stdlib:collection good first issue This is an issue suited for newcomers to become aquianted with working on the codebase. labels May 29, 2024
@fabricerenard12
Copy link

Hi! I'm a first time contributor to crystal and would like to try implementing this feature. Could you assign the issue to me please?

@punteek
Copy link

punteek commented Mar 12, 2025

Hey @Blacksmoke16 it seems this issue has gone unsolved, I'd be happy to take it and take a stab at it as a first-time contributor!

@punteek
Copy link

punteek commented Mar 13, 2025

Hey @Blacksmoke16, I'm working on an attempt on this and had some questions - I think my understanding of some concepts might not be accurate.

Here's my proposed implementations for find and find!

  def find(if_none = nil, offset : Int = 0, &block : T ->)
    offset += size if offset < 0
    return nil if offset < 0
    return index(offset, &block).try { |i| unsafe_fetch(i) } || if_none
  end

  def find!(offset : Int = 0, &block : T ->)
    offset += size if offset < 0
    return nil if offset < 0
    return index(offset, &block).try { |i| unsafe_fetch(i) } || raise Enumerable::NotFoundError.new
  end

Here's how I'm trying to test find inindexable_spec.cr:

  describe "#find" do
    it "finds the first matching element" do
      indexable = SafeIndexable.new(7)
      indexable.find { |i| i > 2 }.should eq 3
    end
  end

I've tried to debug based on this test for a while now, and I've narrowed down the issue to be that in the call to index in find's implementation, yield is always returning nil. However, I'm not sure why! Am I doing block forwarding wrong? Are there ways to debug block execution? Would love to hear if you have any suggestions, thanks.

@punteek
Copy link

punteek commented Mar 13, 2025

I think I was able to change some things and get unblocked, here's an initial PR: #15552

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue This is an issue suited for newcomers to become aquianted with working on the codebase. kind:feature topic:stdlib:collection
Projects
None yet
Development

No branches or pull requests

3 participants