"find" for Slices like "append"

473 views
Skip to first unread message

marti...@programmfabrik.de

unread,
Aug 7, 2017, 10:23:57 AM8/7/17
to golang-nuts
Being fairly new to Go, I am trying to evaluate if it s possible and worth to move our huge C++ codebase over to Go.

1) append(sliceA, my_new_item) sliceA

Why can I not append something to a Slice in place, why does it have to return a new Slice?

2) find(sliceB, item_i_look_for) int, -1 for not found

Why can I not find easily search for an item inside a Slice. Every other language I know has this. And I need this in a generic form.

Or am I missing something entirely here?

Best
Martin

Ian Lance Taylor

unread,
Aug 7, 2017, 10:34:56 AM8/7/17
to marti...@programmfabrik.de, golang-nuts
On Mon, Aug 7, 2017 at 6:45 AM, <marti...@programmfabrik.de> wrote:
> Being fairly new to Go, I am trying to evaluate if it s possible and worth
> to move our huge C++ codebase over to Go.
>
> 1) append(sliceA, my_new_item) sliceA
>
> Why can I not append something to a Slice in place, why does it have to
> return a new Slice?

https://blog.golang.org/slices


> 2) find(sliceB, item_i_look_for) int, -1 for not found
>
> Why can I not find easily search for an item inside a Slice. Every other
> language I know has this. And I need this in a generic form.

As you have probably heard, Go does not have generics. Sorry. The
loop that you need to write is fairly short.

Ian

marti...@programmfabrik.de

unread,
Aug 7, 2017, 10:44:40 AM8/7/17
to golang-nuts, marti...@programmfabrik.de

> Why can I not append something to a Slice in place, why does it have to
> return a new Slice?

https://blog.golang.org/slices

Ok, I will check that, thx.
 

> 2) find(sliceB, item_i_look_for) int, -1 for not found
>
> Why can I not find easily search for an item inside a Slice. Every other
> language I know has this. And I need this in a generic form.

As you have probably heard, Go does not have generics.  Sorry.  The
loop that you need to write is fairly short.

The loop might be short, but I need to know the type of the items in my Slice.

So I need a loop for every datatype!? 

And I need to name it for each type differently? 

Why? 

All I want to be able to do is to find an item in a Slice. 

Why does "append" work with all types then?

Martin
 

Ian Lance Taylor

unread,
Aug 7, 2017, 10:53:46 AM8/7/17
to marti...@programmfabrik.de, golang-nuts
On Mon, Aug 7, 2017 at 7:44 AM, <marti...@programmfabrik.de> wrote:
>>
>> > 2) find(sliceB, item_i_look_for) int, -1 for not found
>> >
>> > Why can I not find easily search for an item inside a Slice. Every other
>> > language I know has this. And I need this in a generic form.
>>
>> As you have probably heard, Go does not have generics. Sorry. The
>> loop that you need to write is fairly short.
>
>
> The loop might be short, but I need to know the type of the items in my
> Slice.
>
> So I need a loop for every datatype!?

Yes.

> And I need to name it for each type differently?

Yes.

> Why?

Because Go does not have generics. There has been a enormous amount
of discussion on this in the past that I won't try to summarize here.
You could read https://golang.org/issue/15292.

> All I want to be able to do is to find an item in a Slice.
>
> Why does "append" work with all types then?

Because it's easy to write a short correct find loop for the type you
are using, but a good implementation of append is a much larger
function. See also http://www.airs.com/blog/archives/559 .

Ian

marti...@programmfabrik.de

unread,
Aug 7, 2017, 11:45:15 AM8/7/17
to golang-nuts, marti...@programmfabrik.de



Because it's easy to write a short correct find loop for the type you
are using, but a good implementation of append is a much larger
function.  See also http://www.airs.com/blog/archives/559 .

So "append" is already done. May I propose to implement a generic "find" function?

That should be possible without coming up with good general generics support, shouldn't it? 

In my everyday programming life a check if an item is inside an Array is very common. Often this
is even built-in syntax (like in Python "if a in array...").

I will have a hard time selling this missing feature to my team.

Martin

Shawn Milochik

unread,
Aug 7, 2017, 11:59:57 AM8/7/17
to golang-nuts
On Mon, Aug 7, 2017 at 11:45 AM, <marti...@programmfabrik.de> wrote:

In my everyday programming life a check if an item is inside an Array is very common. Often this
is even built-in syntax (like in Python "if a in array...").

I will have a hard time selling this missing feature to my team.

Martin

The Go philosophy is explicitly not to give you everything you want.  It is to give you everything you need to build everything you want, like Lego.

Every language is different. Any developer worth their salt won't dismiss a tool out-of-hand for such a trivial reason.

Also, consider the fact that in Python, the same loop is happening. Go just doesn't hide that from the developer, making it easier for us to reason about things like performance. You can write your own "find" function in seconds if you want one.
 
--
☕😎

Jan Mercl

unread,
Aug 7, 2017, 12:08:31 PM8/7/17
to marti...@programmfabrik.de, golang-nuts
On Mon, Aug 7, 2017 at 4:23 PM <marti...@programmfabrik.de> wrote:

> 2) find(sliceB, item_i_look_for) int, -1 for not found

--

-j

marti...@programmfabrik.de

unread,
Aug 8, 2017, 3:06:08 AM8/8/17
to golang-nuts, marti...@programmfabrik.de
Sort.Search is nice, but then the list has to be sorted. Wasnt there a lenghty discussion on how awkward sorting is compared to other languages, and then in 1.8. finally someone implemented 

func Slice(slice interface{}, less func(i, j int) bool)

which already makes it easier to Sort a slice.

So why not come up with

func findFirst(slice interface{}, matches func(i intbool)

or so?

marti...@programmfabrik.de

unread,
Aug 8, 2017, 3:15:05 AM8/8/17
to golang-nuts, Sh...@milochik.com

The Go philosophy is explicitly not to give you everything you want.  It is to give you everything you need to build everything you want, like Lego.

Yeah right, when men still where real men and programmed their own device drivers...

Or take a car, give me parts & tools and I am ready to give you a ride in say a year? 


Every language is different. Any developer worth their salt won't dismiss a tool out-of-hand for such a trivial reason.

No nobody would. But trivial things add up and then people run away or never sign up.

I have learnt to never not listen to your (potential) users.

If a new project comes on board of the Go train, people already have to wrap their heads around new (admittedly interesting) concepts, they have to accept "err != nil" spaghetti, distinction between Array and Slices, make and new, and so on.

Personally I got really interested when I died around your standard library which I really like and it seems to give us exactly what we need, not too much, not too little.

Also, consider the fact that in Python, the same loop is happening. Go just doesn't hide that from the developer, making it easier for us to reason about things like performance. You can write your own "find" function in seconds if you want one.

It just looks awkward:

    contains := false
    for _, n := range excluded_numbers {
      if byte(m) == n {
        contains = true
      }
    }
    if !contains {
       ...

Seriously? 2017?

Martin

 
--
☕😎

Jan Mercl

unread,
Aug 8, 2017, 4:37:16 AM8/8/17
to marti...@programmfabrik.de, golang-nuts
On Tue, Aug 8, 2017 at 9:06 AM <marti...@programmfabrik.de> wrote:

> Sort.Search is nice, but then the list has to be sorted. 

Then it's a simple for range loop.

> So why not come up with

> func findFirst(slice interface{}, matches func(i int) bool)

I hope this never happens, but the standard approach is to fill a 'proposal: foo' at the issue tracker.


--

-j

Christoph Berger

unread,
Aug 8, 2017, 6:33:40 AM8/8/17
to golang-nuts, Sh...@milochik.com
May I ask why you turned to Go in the first place?

Your taunting remarks seem to indicate that you were forced moving to Go; but on the other hand, earlier you indicated that you need to sell Go (or rather, the lack of a feature in Go) to your team, so it seems you are the driving force behind moving to Go. 

In the latter case, if Go does not fulfill your expectations (and given you still have free choice), then have you thought about choosing a language with the features you need instead? I am sure no one here would seriously recommend Go as a "one-size-fits-all" language. Go does not have generics nor exceptions, so if you want these features badly, then Go isn't for you.

roger peppe

unread,
Aug 8, 2017, 7:52:44 AM8/8/17
to marti...@programmfabrik.de, golang-nuts, Sh...@milochik.com
I'd usually write that as as separate function:

func isExcluded(ns []byte, m byte) bool {
     for _, n := range ns {
          if  n == m {
               return true
          }
     }
     return false
}

For that *particular* case, you could always use
bytes.Index(excluded_numbers, byte(m)) >= 0
though.

Yes, it feels a little tedious sometimes, but if you
add up the number of times you actually have to do this,
it's generally not too much. There's a trade-off going
on here.

  cheers,
    rog.


Martin

 
--
☕😎

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jakob Borg

unread,
Aug 9, 2017, 2:35:31 AM8/9/17
to marti...@programmfabrik.de, golang-nuts
On 8 Aug 2017, at 09:06, marti...@programmfabrik.de wrote:
>
> So why not come up with
>
> func findFirst(slice interface{}, matches func(i int) bool) int
>
> or so?

For what it's worth, it's not super difficult to implement this function once and for all if it's something that would make your life easier. It not being in the standard library today doesn't mean it's something you're forbidden from doing. The gain is just three lines of trivial code per invocation, but it's certainly possible.

Using it is going to be more expensive than the obvious loop so I would be surprised to see this function surface in the stdlib.

//jb

Volker Dobler

unread,
Aug 9, 2017, 3:01:54 AM8/9/17
to golang-nuts, marti...@programmfabrik.de
On Tuesday, 8 August 2017 09:06:08 UTC+2, marti...@programmfabrik.de wrote:
[...] So why not come up with

func findFirst(slice interface{}, matches func(i intbool)

or so?

Well, this looks harmless but the major work is not in iterating the slice
but in handling all the subtle cases in match. E.g. in numerical code
the NaNs, the +/-Infs, the values just formally != 0 (but too small for
general computations). For strings the various Unicode normalisation
forms, BOMs, etc. For objects without simple value semantics like
streams, channel or network connections where matching them might
change their state and might no be easily undoable.

So if match is simple like func(i int){ s[i].Id == 7 } then the proposed
findFirst function saves 2 or three lines of code. Code almost any
team gets right on the first try in 10 minutes.
But if the match is non-trivial then the 2 lines saved is not a game
changer.

So why bother?

One more point: There might be no match in the slice so this findFirst
would have to return -1 to indicate "not found". If this can happen your
code would have to handle this as well; which makes the ratio of
"lines you have to write to cover the business logic" to "lines saved
by having a generic findFirst" even bigger.
(Of course one could go down the function proliferation road and add a
mustFindFirst...)

V.

marti...@programmfabrik.de

unread,
Aug 9, 2017, 3:10:30 AM8/9/17
to golang-nuts, marti...@programmfabrik.de
Volker thanks for the reply. Coming from Python I can do a simple test like

if item in my_array:
   # do something
else:
   # do something else

I can understand that it cannot be that easy in Go (and then, why not, "equals" is already defined), but image you
have to use a for-loop every time or a specialized function every time. That is awkward and unnecessary.

I agree, that the more complex idea to have a "findFirst" with a dedicated matching function
is more complicated, but hey, sort.Slice is the same thing, isn't it? It takes a comparison func to return -1, 0, or 1.

func findFirst(slice interface{}, matches func(i int) int)

Just my 0.02. 


Jan Mercl

unread,
Aug 9, 2017, 3:54:54 AM8/9/17
to marti...@programmfabrik.de, golang-nuts



On Wed, Aug 9, 2017 at 9:10 AM <marti...@programmfabrik.de> wrote:

> ...., why not, "equals" is already defined), ...

Does []float64{1, NaN, 2} contain a NaN? Using the defined equality the answer is no.

--

-j

Tyler Compton

unread,
Aug 9, 2017, 4:24:20 AM8/9/17
to Jan Mercl, marti...@programmfabrik.de, golang-nuts
I think Christoph is asking the right question here. If the code you're writing requires you to search through collections of many different types all across the code base, it may make sense to use a language like Python with in-language support for searching. I think the more likely scenario is that searching through collections is a trivial part of the overall program.

I think it would be worth working with the language for a bit and providing an experience report perhaps alongside a proposal if this issue ends up being as serious as you expect.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Volker Dobler

unread,
Aug 9, 2017, 6:36:06 AM8/9/17
to golang-nuts, marti...@programmfabrik.de

On Wednesday, 9 August 2017 09:10:30 UTC+2, marti...@programmfabrik.de wrote:
Volker thanks for the reply. Coming from Python I can do a simple test like

if item in my_array:
   # do something
else:
   # do something else

I can understand that it cannot be that easy in Go (and then, why not, "equals" is already defined), but image you
have to use a for-loop every time or a specialized function every time. That is awkward and unnecessary.

See Jan's comment. This is the exact problem here.

(I think you may well assume that most people in this list know
and have experience in Python and possible a dozen other
languages too. So there is not much need showing how
something is done in Python: The question is how it could
be done in Go and we argue that it cannot be made that
simple.)

 
I agree, that the more complex idea to have a "findFirst" with a dedicated matching function
is more complicated, but hey, sort.Slice is the same thing, isn't it? It takes a comparison func to return -1, 0, or 1.

func findFirst(slice interface{}, matches func(i int) int)

There is a major difference in sort.SortSlice and your proposed sort.FindFirst:
The complicated logic is in different places.
For SortSlice it is in the sorting code (and not in the less function).
For FindFirst it is in the match function (and not in finding code).
That's why it is sensible to have a function doing the heavy lifting,
the sorting but hiding a trivial (because all logic is in match) for loop
for finding the first element is not needed.

V.
Message has been deleted

Haddock

unread,
Aug 9, 2017, 7:16:12 AM8/9/17
to golang-nuts

as

unread,
Aug 9, 2017, 11:02:50 AM8/9/17
to golang-nuts
For loops are not your enemy. Cyclomatic complexity in cute_loop_functions is. The for loop tells you something is happening. Your contains loop is always running for the entire length of the slice.

Ian Lance Taylor

unread,
Aug 9, 2017, 11:53:45 AM8/9/17
to golang-nuts
Here is a generic Find function that works for all types, using the
language defined == operator. It will panic if invoked incorrectly.

package main

import (
"fmt"
"math"
"reflect"
)

// Find returns the index of val in the slice s, or -1 if not found.
func Find(s, val interface{}) int {
sv := reflect.ValueOf(s)
l := sv.Len()
for i := 0; i < l; i++ {
if sv.Index(i).Interface() == val {
return i
}
}
return -1
}

var tests = []struct {
s, v interface{}
want int
}{
{[]int{1, 2, 3}, 2, 1},
{[]int{1, 2, 3}, 4, -1},
{[]string{"a", "b"}, "a", 0},
{[]float64{0, 1, math.NaN(), 2}, 2.0, 3},
{[]float64{0, 1, math.NaN(), 2}, math.NaN(), -1},
}

func main() {
for _, t := range tests {
if got := Find(t.s, t.v); got != t.want {
fmt.Printf("Find(%v, %v) = %d, want %d\n",
t.s, t.v, got, t.want)
}
}
}

mar...@gmail.com

unread,
Sep 4, 2017, 6:56:24 PM9/4/17
to golang-nuts
Ian, 

thanks for the "generic" function. I appreciate.

I think such a function would provide beautiful symmetry to the already existing "append" function Go has.

Martin

DV

unread,
Sep 6, 2017, 10:04:22 AM9/6/17
to golang-nuts
Go has never been a "let's include all possible methods for all possible use-cases for all possible users" type of language. It's just not in its DNA. You seem to like Python - so why not stick with Python? Do lots of C++ people complain about how Javascript doesn't let you do pointer arithmetic or multiple inheritance easily and expect Javascript to include such functionality? 

I write Python *and* Go on a daily basis, and appreciate the weaknesses and strengths of both languages - I can't say I think that one should be a strict subset of the other. Python has multiple inheritance and duck-typing as well - should those be part of Go to '"ease" adoption by Python programmers? 

"append" exists for slices for the same reason that "delete" exists for maps - difficult to implement correctly and efficiently, hard to live without it on a daily basis if using those data-structures. Before "append", there was a std. lib Vector type (circa 2009 or so) and removing Vector and adding "append" was a huge quality-of-life improvement. 

"find" ? Not difficult to implement correctly, *not* hard to live without it -> small chance your proposal makes the cut. Barrier of entry is high. Just how it is. 

Rich

unread,
Sep 7, 2017, 3:53:27 PM9/7/17
to golang-nuts
For #1 -- There is a reason for this, I could try to explain it but I believe a guy named Todd McLeod would explain it better: https://www.youtube.com/user/toddmcleod/search?query=slice

For #2 -- Go is easily extendable.  My suggestion if you want a find, write one.   I know that kinda sounds harsh but it's a great way to learn the language by writing your first extension that you can import with each project. 
Reply all
Reply to author
Forward
0 new messages