Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the correct way to find the min between two integers in Go?

Tags:

go

I imported the math library in my program, and I was trying to find the minimum of three numbers in the following way:

v1[j+1] = math.Min(v1[j]+1, math.Min(v0[j+1]+1, v0[j]+cost))

where v1 is declared as:

t := "stackoverflow" v1 := make([]int, len(t)+1) 

However, when I run my program I get the following error:

./levenshtein_distance.go:36: cannot use int(v0[j + 1] + 1) (type int) as type float64 in argument to math.Min 

I thought it was weird because I have another program where I write

fmt.Println(math.Min(2,3))

and that program outputs 2 without complaining.

so I ended up casting the values as float64, so that math.Min could work:

v1[j+1] = math.Min(float64(v1[j]+1), math.Min(float64(v0[j+1]+1), float64(v0[j]+cost))) 

With this approach, I got the following error:

./levenshtein_distance.go:36: cannot use math.Min(int(v1[j] + 1), math.Min(int(v0[j + 1] + 1), int(v0[j] + cost))) (type float64) as type int in assignment 

so to get rid of the problem, I just casted the result back to int

I thought this was extremely inefficient and hard to read:

v1[j+1] = int(math.Min(float64(v1[j]+1), math.Min(float64(v0[j+1]+1), float64(v0[j]+cost)))) 

I also wrote a small minInt function, but I think this should be unnecessary because the other programs that make use of math.Min work just fine when taking integers, so I concluded this has to be a problem of my program and not the library per se.

Is there anything that I'm doing terrible wrong?

Here's a program that you can use to reproduce the issues above, line 36 specifically: package main

import (     "math" )  func main() {     LevenshteinDistance("stackoverflow", "stackexchange") }  func LevenshteinDistance(s string, t string) int {     if s == t {         return 0     }     if len(s) == 0 {         return len(t)     }     if len(t) == 0 {         return len(s)     }      v0 := make([]int, len(t)+1)     v1 := make([]int, len(t)+1)      for i := 0; i < len(v0); i++ {         v0[i] = i     }      for i := 0; i < len(s); i++ {         v1[0] = i + 1         for j := 0; j < len(t); j++ {             cost := 0             if s[i] != t[j] {                 cost = 1             }             v1[j+1] = int(math.Min(float64(v1[j]+1), math.Min(float64(v0[j+1]+1), float64(v0[j]+cost))))         }          for j := 0; j < len(v0); j++ {             v0[j] = v1[j]         }     }     return v1[len(t)] } 
like image 690
ILikeTacos Avatar asked Dec 17 '14 00:12

ILikeTacos


2 Answers

Nope, I think writing something like that is fine: for instance, the stdlib's sort.go does it near the top of the file:

func min(a, b int) int {     if a < b {         return a     }     return b } 

math.Min(2, 3) happened to work because numeric constants in Go are untyped. Beware of treating float64s as a universal number type in general, though, since integers above 2^53 will get rounded if converted to float64.

Although it does not work on stable Go as I'm writing this, in the Go 1.18 beta you can write a generic min function which is just as efficient at run time as the hand-coded single-type version.

There's been discussion of updating the stdlib to add generic versions of existing functions, but if that happens it won't be until a later version.

like image 77
twotwotwo Avatar answered Oct 13 '22 21:10

twotwotwo


There is no built-in min or max function for integers, but it’s simple to write your own. Thanks to support for variadic functions we can even compare more integers with just one call:

func MinOf(vars ...int) int {     min := vars[0]      for _, i := range vars {         if min > i {             min = i         }     }      return min } 

Usage:

MinOf(3, 9, 6, 2) 

Similarly here is the max function:

func MaxOf(vars ...int) int {     max := vars[0]      for _, i := range vars {         if max < i {             max = i         }     }      return max } 
like image 43
Sergiu Sandrean Avatar answered Oct 13 '22 20:10

Sergiu Sandrean