Respuestas:
Ambos estilos se usan dentro de las bibliotecas estándar de Go.
if len(s) > 0 { ... }
se puede encontrar en el strconv
paquete: http://golang.org/src/pkg/strconv/atoi.go
if s != "" { ... }
se puede encontrar en el encoding/json
paquete: http://golang.org/src/pkg/encoding/json/encode.go
Ambos son idiomáticos y son lo suficientemente claros. Es más una cuestión de gusto personal y de claridad.
Russ Cox escribe en un hilo de nueces de golang :
El que aclara el código.
Si estoy a punto de ver el elemento x, normalmente escribo
len (s)> x, incluso para x == 0, pero si me importa
"¿es esta cadena específica" tiendo a escribir s == "".Es razonable suponer que un compilador maduro compilará
len (s) == 0 y s == "" en el mismo código eficiente.
...Haz el código claro.
Como se señaló en la respuesta de Timmmm , el compilador Go genera código idéntico en ambos casos.
len
para verificar cadenas vacías / no vacías. Me gusta este compromiso de Brad Fitzpatrick. Me temo que todavía es una cuestión de gusto y claridad;)
len(v) > 0
en h2_bundle.go (línea 2702). Creo que no se muestra automáticamente, ya que se genera desde golang.org/x/net/http2.
Esto parece ser una microoptimización prematura. El compilador es libre de producir el mismo código para ambos casos o al menos para estos dos
if len(s) != 0 { ... }
y
if s != "" { ... }
porque la semántica es claramente igual.
Verificar la longitud es una buena respuesta, pero también podría tener en cuenta una cadena "vacía" que también es solo un espacio en blanco. No está "técnicamente" vacío, pero si le interesa verificar:
package main
import (
"fmt"
"strings"
)
func main() {
stringOne := "merpflakes"
stringTwo := " "
stringThree := ""
if len(strings.TrimSpace(stringOne)) == 0 {
fmt.Println("String is empty!")
}
if len(strings.TrimSpace(stringTwo)) == 0 {
fmt.Println("String two is empty!")
}
if len(stringTwo) == 0 {
fmt.Println("String two is still empty!")
}
if len(strings.TrimSpace(stringThree)) == 0 {
fmt.Println("String three is empty!")
}
}
TrimSpace
asignará y copiará una nueva cadena de la cadena original, por lo que este enfoque introducirá ineficiencias a escala.
s
es de tipo cadena, s[0:i]
devuelve una nueva copia. Las cadenas son inmutables en Go, entonces, ¿necesita crear una copia aquí?
strings.TrimSpace( s )
no provocará una nueva asignación de cadena y copia de caracteres si la cadena no necesita recorte, pero si la cadena necesita recorte, se invocará la copia adicional (sin caracteres de espacio en blanco).
gocritic
linter sugiere usar en strings.TrimSpace(str) == ""
lugar de la verificación de longitud.
Suponiendo que los espacios vacíos y todos los espacios en blanco iniciales y finales deben eliminarse:
import "strings"
if len(strings.TrimSpace(s)) == 0 { ... }
Porque :
len("") // is 0
len(" ") // one empty space is 1
len(" ") // two empty spaces is 2
< 1
+1
A partir de ahora, el compilador Go genera un código idéntico en ambos casos, por lo que es cuestión de gustos. GCCGo genera un código diferente, pero casi nadie lo usa, así que no me preocuparía por eso.
Sería más limpio y menos propenso a errores usar una función como la siguiente:
func empty(s string) bool {
return len(strings.TrimSpace(s)) == 0
}
Solo para agregar más para comentar
Principalmente sobre cómo hacer pruebas de rendimiento.
Hice pruebas con el siguiente código:
import (
"testing"
)
var ss = []string{"Hello", "", "bar", " ", "baz", "ewrqlosakdjhf12934c r39yfashk fjkashkfashds fsdakjh-", "", "123"}
func BenchmarkStringCheckEq(b *testing.B) {
c := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
for _, s := range ss {
if s == "" {
c++
}
}
}
t := 2 * b.N
if c != t {
b.Fatalf("did not catch empty strings: %d != %d", c, t)
}
}
func BenchmarkStringCheckLen(b *testing.B) {
c := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
for _, s := range ss {
if len(s) == 0 {
c++
}
}
}
t := 2 * b.N
if c != t {
b.Fatalf("did not catch empty strings: %d != %d", c, t)
}
}
func BenchmarkStringCheckLenGt(b *testing.B) {
c := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
for _, s := range ss {
if len(s) > 0 {
c++
}
}
}
t := 6 * b.N
if c != t {
b.Fatalf("did not catch empty strings: %d != %d", c, t)
}
}
func BenchmarkStringCheckNe(b *testing.B) {
c := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
for _, s := range ss {
if s != "" {
c++
}
}
}
t := 6 * b.N
if c != t {
b.Fatalf("did not catch empty strings: %d != %d", c, t)
}
}
Y los resultados fueron:
% for a in $(seq 50);do go test -run=^$ -bench=. --benchtime=1s ./...|grep Bench;done | tee -a log
% sort -k 3n log | head -10
BenchmarkStringCheckEq-4 150149937 8.06 ns/op
BenchmarkStringCheckLenGt-4 147926752 8.06 ns/op
BenchmarkStringCheckLenGt-4 148045771 8.06 ns/op
BenchmarkStringCheckNe-4 145506912 8.06 ns/op
BenchmarkStringCheckLen-4 145942450 8.07 ns/op
BenchmarkStringCheckEq-4 146990384 8.08 ns/op
BenchmarkStringCheckLenGt-4 149351529 8.08 ns/op
BenchmarkStringCheckNe-4 148212032 8.08 ns/op
BenchmarkStringCheckEq-4 145122193 8.09 ns/op
BenchmarkStringCheckEq-4 146277885 8.09 ns/op
Las variantes efectivas generalmente no alcanzan el tiempo más rápido y solo hay una diferencia mínima (aproximadamente 0.01ns / op) entre la velocidad máxima de la variante.
Y si miro el registro completo, la diferencia entre los intentos es mayor que la diferencia entre las funciones de referencia.
Además, no parece haber ninguna diferencia apreciable entre BenchmarkStringCheckEq y BenchmarkStringCheckNe o BenchmarkStringCheckLen y BenchmarkStringCheckLenGt, incluso si estas últimas variantes deberían aumentar 6 veces en lugar de 2 veces.
Puede intentar obtener algo de confianza sobre el mismo rendimiento agregando pruebas con prueba modificada o bucle interno. Esto es más rápido:
func BenchmarkStringCheckNone4(b *testing.B) {
c := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
for _, _ = range ss {
c++
}
}
t := len(ss) * b.N
if c != t {
b.Fatalf("did not catch empty strings: %d != %d", c, t)
}
}
Esto no es más rápido:
func BenchmarkStringCheckEq3(b *testing.B) {
ss2 := make([]string, len(ss))
prefix := "a"
for i, _ := range ss {
ss2[i] = prefix + ss[i]
}
c := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
for _, s := range ss2 {
if s == prefix {
c++
}
}
}
t := 2 * b.N
if c != t {
b.Fatalf("did not catch empty strings: %d != %d", c, t)
}
}
Ambas variantes suelen ser más rápidas o más lentas que la diferencia entre las pruebas principales.
También sería bueno generar cadenas de prueba (ss) utilizando un generador de cadenas con distribución relevante. Y tienen longitudes variables también.
Por lo tanto, no tengo ninguna confianza en la diferencia de rendimiento entre los métodos principales para probar la cadena vacía en go.
Y puedo decir con cierta confianza, es más rápido no probar una cadena vacía que probar una cadena vacía. Y también es más rápido probar una cadena vacía que probar 1 cadena de caracteres (variante de prefijo).
Según las pautas oficiales y desde el punto de vista del rendimiento, parecen equivalentes ( respuesta ANisus ), s! = "" Sería mejor debido a una ventaja sintáctica. s! = "" fallará en tiempo de compilación si la variable no es una cadena, mientras que len (s) == 0 pasará por varios otros tipos de datos.
len()
requiere un poco de trabajo extra. SIN EMBARGO, una cosa que solíamos hacer en C era lanzar el lado izquierdo a una const
o colocar la cadena estática en el lado izquierdo del operador para evitar que s == "" se convierta en s = "", lo cual en la sintaxis de C es aceptable. .. y probablemente golang también. (ver el extendido si)
Esto sería más eficaz que recortar toda la cadena, ya que solo necesita verificar al menos un carácter que no sea un espacio existente
// Strempty checks whether string contains only whitespace or not
func Strempty(s string) bool {
if len(s) == 0 {
return true
}
r := []rune(s)
l := len(r)
for l > 0 {
l--
if !unicode.IsSpace(r[l]) {
return false
}
}
return true
}
Creo que la mejor manera es comparar con una cadena en blanco
BenchmarkStringCheck1 está comprobando con una cadena en blanco
BenchmarkStringCheck2 está comprobando con len zero
Compruebo con la comprobación de cadena vacía y no vacía. Puede ver que verificar con una cadena en blanco es más rápido.
BenchmarkStringCheck1-4 2000000000 0.29 ns/op 0 B/op 0 allocs/op
BenchmarkStringCheck1-4 2000000000 0.30 ns/op 0 B/op 0 allocs/op
BenchmarkStringCheck2-4 2000000000 0.30 ns/op 0 B/op 0 allocs/op
BenchmarkStringCheck2-4 2000000000 0.31 ns/op 0 B/op 0 allocs/op
Código
func BenchmarkStringCheck1(b *testing.B) {
s := "Hello"
b.ResetTimer()
for n := 0; n < b.N; n++ {
if s == "" {
}
}
}
func BenchmarkStringCheck2(b *testing.B) {
s := "Hello"
b.ResetTimer()
for n := 0; n < b.N; n++ {
if len(s) == 0 {
}
}
}
if mystring != "" { }
es la mejor, preferida e idiomática HOY. La razón por la que la biblioteca estándar contiene lo contrario es porque fue escrita antes de 2010 cuando lalen(mystring) == 0
optimización tenía sentido.