¿Por qué se devuelve True cuando se comprueba si una cadena vacía está en otra?

Mi cerebro limitado no puede entender por qué sucede esto:

>>> print '' in 'lolsome' True 

En PHP, una comparación equivalente devuelve falso:

 var_dump(strpos('', 'lolsome')); 

De la documentación :

Para los tipos Unicode y de cadena, x in y es verdadero si y solo si x es una subcadena de y . Una prueba equivalente es y.find(x) != -1 . Tenga en cuenta, xey no tienen que ser del mismo tipo; en consecuencia, u'ab' in 'abc' devolverá True . Las cadenas vacías siempre se consideran una subcadena de cualquier otra cadena, por lo que "" in "abc" devolverá True .

Desde que miras tu llamada print , estás usando 2.x.

Para profundizar, mira el código de bytes:

 >>> def answer(): ... '' in 'lolsome' >>> dis.dis(answer) 2 0 LOAD_CONST 1 ('') 3 LOAD_CONST 2 ('lolsome') 6 COMPARE_OP 6 (in) 9 POP_TOP 10 LOAD_CONST 0 (None) 13 RETURN_VALUE 

COMPARE_OP es donde realizamos nuestra operación booleana y buscamos el código fuente para revelar dónde ocurre la comparación:

  TARGET(COMPARE_OP) { w = POP(); v = TOP(); if (PyInt_CheckExact(w) && PyInt_CheckExact(v)) { /* INLINE: cmp(int, int) */ register long a, b; register int res; a = PyInt_AS_LONG(v); b = PyInt_AS_LONG(w); switch (oparg) { case PyCmp_LT: res = a < b; break; case PyCmp_LE: res = a <= b; break; case PyCmp_EQ: res = a == b; break; case PyCmp_NE: res = a != b; break; case PyCmp_GT: res = a > b; break; case PyCmp_GE: res = a >= b; break; case PyCmp_IS: res = v == w; break; case PyCmp_IS_NOT: res = v != w; break; default: goto slow_compare; } x = res ? Py_True : Py_False; Py_INCREF(x); } else { slow_compare: x = cmp_outcome(oparg, v, w); } Py_DECREF(v); Py_DECREF(w); SET_TOP(x); if (x == NULL) break; PREDICT(POP_JUMP_IF_FALSE); PREDICT(POP_JUMP_IF_TRUE); DISPATCH(); } 

y donde cmp_outcome está en el mismo archivo , es fácil encontrar nuestra siguiente pista:

 res = PySequence_Contains(w, v); 

que está en abstract.c :

 { Py_ssize_t result; if (PyType_HasFeature(seq->ob_type, Py_TPFLAGS_HAVE_SEQUENCE_IN)) { PySequenceMethods *sqm = seq->ob_type->tp_as_sequence; if (sqm != NULL && sqm->sq_contains != NULL) return (*sqm->sq_contains)(seq, ob); } result = _PySequence_IterSearch(seq, ob, PY_ITERSEARCH_CONTAINS); return Py_SAFE_DOWNCAST(result, Py_ssize_t, int); } 

y para obtener air de la fuente, encontramos la siguiente función en la documentación :

 objobjproc PySequenceMethods.sq_contains 

Esta función puede ser utilizada por PySequence_Contains() y tiene la misma firma. Esta ranura se puede dejar en NULL , en este caso, PySequence_Contains() simplemente atraviesa la secuencia hasta que encuentra una coincidencia.

Y más abajo en la misma documentación :

 int PySequence_Contains(PyObject *o, PyObject *value) 

Determine si o contiene valor . Si un elemento en o es igual a valor , devuelve 1 , de lo contrario devuelve 0 . En caso de error, devuelva -1 . Esto es equivalente al value in o expresión de Python value in o .

Donde '' no es null , se puede pensar que la secuencia 'lolsome' lo contiene.

Citando de la documentación strpos del PHP ,

 mixed strpos ( string $haystack , mixed $needle [, int $offset = 0 ] ) 

Encuentre la posición numérica de la primera aparición de la needle en la cadena del haystack .

Entonces, lo que realmente has intentado es similar al constructo de Python que se ve a continuación.

 >>> print 'lolsome' in '' False 

Entonces, deberías haber escrito como se muestra a continuación para tener la comparación correspondiente en PHP

 var_dump(strpos('lolsome', '')); 

Incluso entonces emite una advertencia y devuelve false .

Advertencia de PHP: strpos() : aguja vacía en /home/thefourtheye/Desktop/Test.php en la línea 3

bool(false)

Cavé más profundo y encontré el código fuente correspondiente a la función strpos ,

  if (!Z_STRLEN_P(needle)) { php_error_docref(NULL, E_WARNING, "Empty needle"); RETURN_FALSE; } 

Consideran que la cadena vacía que se busca es un caso problemático. Entonces, están emitiendo una advertencia y devolviendo false . Aparte de esto, no pude encontrar ningún documento que explique por qué se considera un problema.

En lo que respecta a Python, este comportamiento está bien definido en la sección de Comparaciones ,

Las cadenas vacías siempre se consideran una subcadena de cualquier otra cadena, por lo que "" in "abc" devolverá True .

Básicamente, desde matemáticas:

El conjunto vacío es un subconjunto de cada conjunto.

La misma lógica funciona aquí. Puedes considerar '' un conjunto vacío. Y, por lo tanto, es un subconjunto de cada conjunto de cadenas , ya que deben ser del mismo tipo.

 >>> a = "" >>> b = "Python" >>> a in b True >>> set(a).issubset(b) True >>> a = set() #empty set >>> b = set([1,2,3]) >>> a.issubset(b) True >>> 

¡Pero ten cuidado! Un subconjunto y una membresía son cosas diferentes .

introduzca la descripción de la imagen aquí

La cadena vacía es la cadena única de longitud cero.
La cadena vacía es el elemento de identidad de la operación de concatenación.
La cadena vacía precede a cualquier otra cadena bajo orden lexicográfico, porque es la más corta de todas las cadenas.
La cadena vacía es una cadena legítima, sobre la cual deberían funcionar la mayoría de las operaciones de cadena.
Wikipedia

  > strlen(""); => 0 > "a" . "" == "a"; => true > "" . "a" == "a"; => true > "" < "\0"; => true 

Desde arriba, parece que PHP trata la cadena vacía como una cadena válida.

 > strstr("lolsome", ""); strstr(): Empty needle :1 

Pero no parece considerar la cadena vacía como totalmente legítima. Probablemente, PHP es el único idioma que no permite que la subcadena que se busca dentro de una cadena sea una cadena vacía.

¿Es un mecanismo defensivo? Obviamente, los progtwigdores no tienen que proteger la aguja con if . Si es así, ¿por qué otros idiomas permiten que pase esta prueba? Los diseñadores de idiomas tienen que responder

¿De qué está hecha una cadena de Python?

 >>> ''.count('') 1 

Obviamente, la cadena vacía tiene una cadena vacía.

 >>> 'a'.count('') 2 

Una cadena de elementos tiene dos signos vacíos.

 >>> 'ab'.count('') 3 

Así que parece que la cadena Python es una concatenación de cadenas de un elemento. Cada elemento de una cadena está intercalado entre dos cadenas vacías.

 >>> "lolsome".split('') Traceback (most recent call last): File "", line 1, in  ValueError: empty separator 

Pero aquí Python contradice la validez de la cadena vacía. ¿Es un error?
Ruby y JavaScript pasan la prueba aquí.

  > "lolsome".split("") => ["l", "o", "l", "s", "o", "m", "e"] 

He comstackdo varios ejemplos de lenguaje del código de Rosetta , es interesante notar que todos permiten la cadena vacía en la búsqueda de subcadenas y devuelven verdadero.

AWK

 awk 'BEGIN { print index("lolsome", "") != 0 }' 

do

 int main() { printf("%d\n", strstr("lolsome", "") != NULL); return 0; } 

C ++

 #include  #include  int main() { std::string s = "lolsome"; std::cout << (s.find("") != -1) << "\n"; return 0; } 

DO#

 using System; class MainClass { public static void Main (string[] args) { string s = "lolsome"; Console.WriteLine(s.IndexOf("", 0, s.Length) != -1); } } 

Clojure

 (println (.indexOf "lolsome" "")) 

Ir

 package main import ( "fmt" "strings" ) func main() { fmt.Println(strings.Index("lolsome", "") != -1) } 

Maravilloso

 println 'lolsome'.indexOf('') 

devuelve 0, en caso de error devuelve -1

Java

 class Main { public static void main(String[] args) { System.out.println("lolsome".indexOf("") != -1); } } 

JavaScript

 "lolsome".indexOf("") != -1 

Lua

 s = "lolsome" print(s:find "" ~= nil) 

Perl

 print index("lolsome", "") != -1; 

Pitón

 "lolsome".find("") != -1 

Rubí

 "lolsome".index("") != nil 

Supongamos que tienes 2 stacks de objetos similares, por ejemplo, las mejores estrofas de tu poeta favorito, de 5 y 2 respectivamente. ¿El conjunto más grande contiene un conjunto más pequeño? Cómo verificar: 1) para cualquier estrofa en la stack más pequeña, puede encontrarla en una más grande. 2) la stack más pequeña no contiene algo que está ausente en una más grande.

Entonces podemos usar este pseudocódigo para verificar:

 for object in smaller: if object not in bigger: return 'we found object from smaller absent in bigger' else: go to next object return 'all is ok - all objects from smaller are in bigger' 

Si no ha encontrado un objeto de este tipo, llega al final de algo y piensa que más pequeño es un subconjunto de más grande.

Ahora imagina que stack más pequeña es de 0 estrofas. Aplicando las mismas reglas anteriores, realizamos 0 controles y tampoco encontramos objetos de menor tamaño, lo que está ausente en mayor.

Por lo tanto, es correcto y útil considerar la cadena vacía como un subconjunto de cualquier otra cadena. Incluso a si mismo. Y esto se realiza en python.

  >>> '' in 'adfsf' True >>> '' in '' True