在PHP中连接n个数组的值

7
我是一名有用的助手,可以为您翻译文本。
我有许多未知数量的数组,每个数组中包含未知数量的单词。我想将每个列表中的值连接起来,以便将单词的所有可能变化存储到最终数组中。
例如,如果数组1包含:
dog
cat

数组2包含以下内容:

food
tooth

数组3包含:

car
bike

我希望输出结果为:
dog food car
dog food bike
dog tooth car
dog tooth bike
cat food car
cat food bike
cat tooth car
cat tooth bike

可能会有超过3个列表,每个列表很可能都有超过2个单词。

我想用PHP来实现这个功能。

如果我知道列表的数量,我知道如何做到这一点,尽管这可能不是最有效的方法。但如果您知道数组的数量,则嵌套的foreach循环可以工作。如果您不知道呢?还有哪些方法可以解决这个问题,即使有100个100个单词的数组。或者是1000?

谢谢!


你是否也需要 狗粮狗牙猫粮猫牙 或者只是所有数组的组合? - Gordon
只是针对这个特定问题的所有数组的组合,不过看到这也很有趣。 - hookedonwinter
如果您想要处理100个大小为100的数组,试图直接生成一个数组会导致严重的内存问题。组合数量是非常巨大的,这是笛卡尔积。您需要一个迭代器。这种方法以时间换空间,速度较慢,但可以让您保持在内存限制范围内。我本来想发一个链接,但那是这里的活跃成员网站。如果他自己没有出现并回答,我就会发布链接。否则,我不想窃取他的荣耀。 - goat
很酷,谢谢Chris。我期待着它。我怀疑我永远不会有100x100的地图。最可能的情况是不到20个项目的5个数组。但从理论上讲,我对大规模想法很感兴趣。 - hookedonwinter
可能是重复的问题:使用PHP关联数组找到笛卡尔积 - undefined
4个回答

10

您可以将所有的单词数组放入一个数组中,然后使用递归函数进行操作,如下所示:

function concat(array $array) {
    $current = array_shift($array);
    if(count($array) > 0) {
        $results = array();
        $temp = concat($array);
        foreach($current as $word) {
          foreach($temp as $value) {
            $results[] =  $word . ' ' . $value;
          }
        }
        return $results;           
    }
    else {
       return $current;
    }
}

$a = array(array('dog', 'cat'), array('food', 'tooth'), array('car', 'bike'));

print_r(concat($a));

返回如下结果:

Array
(
    [0] => dog food car
    [1] => dog food bike
    [2] => dog tooth car
    [3] => dog tooth bike
    [4] => cat food car
    [5] => cat food bike
    [6] => cat tooth car
    [7] => cat tooth bike
)

但我猜对于大数组来说这种方式表现不佳,因为输出数组将非常大。


为了解决这个问题,你可以直接输出组合,使用类似的方法:

function concat(array $array, $concat = '') {
    $current = array_shift($array);

    $current_strings = array();

    foreach($current as $word) {
            $current_strings[] = $concat . ' ' . $word;
    }

    if(count($array) > 0) {
        foreach($current_strings as $string) {
            concat($array, $string);
        }       
    }
    else {
      foreach($current_strings as $string) {
          echo $string . PHP_EOL;
      }   
    }
}

concat(array(array('dog', 'cat'), array('food', 'tooth'), array('car', 'bike')));

这将会得到:

dog food car
dog food bike
dog tooth car
dog tooth bike
cat food car
cat food bike
cat tooth car
cat tooth bike

使用这种方法也很容易获得“子串连接”。只需在concat($array, $string);之前插入echo $string . PHP_EOL;即可输出:

 dog
 dog food
 dog food car
 dog food bike
 dog tooth
 dog tooth car
 dog tooth bike
 cat
 cat food
 cat food car
 cat food bike
 cat tooth
 cat tooth car
 cat tooth bike

Felix - 这在小数组上运行得很好。我刚刚尝试了5个长度为100的数组,结果是:致命错误:在第9行中分配11字节时已用尽134217728字节的允许内存大小- 我不知道我会有那么多单词,所以你的解决方案对我正在做的事情可能很好用。但是对于较大的数组肯定会有一些滞后。感谢你的想法! - hookedonwinter
@Felix 还没有。刚看到。谢谢! - hookedonwinter
1
挑剔一点:第二个解决方案并不是“迭代”的。你只是保留并重复使用中间结果(就像缓存一样)。尽管foreach($current_strings as $string) { concat($array, $string);仍然是递归。 - VolkerK
@VolkerK:好的,我不确定这一点,谢谢。但是递归不使用调用函数的返回值是否有特殊术语? - Felix Kling
如果有,那我不知道;今天/这周我已经用完了我的“几乎但并不完全正确”的解释,包括之前的回答和评论;-)最好问一个真正懂的人。 - VolkerK

5

您可以枚举结果集的元素,即对于0....(元素数)-1之间的每个整数,您可以告诉要返回哪个元素(即有一种自然顺序)。 对于给定的示例:

0 => array1[0], array2[0], array3[0]
1 => array1[0], array2[0], array3[1]
2 => array1[0], array2[1], array3[0]
7 => array1[1], array2[1], array3[1]

您只需要一个(整数)索引n和一个将索引“翻译”为(自然排序的)集合的第n个元素的函数。由于您只需要一个整数来存储当前状态,所以当您有多个/大型数组时,内存消耗不会“爆炸”。正如Chris在他的评论中所说,您在使用较小的集合时会换取速度,以降低内存消耗。(虽然我认为-通过php的实现方式-这也是一个合理快速的解决方案。)
$array1 = array('dog', 'cat');
$array2 = array('food', 'tooth');
$array3 = array('car', 'bike');

function foo( $key /* , ... */ ) {
  $params = func_get_args();
  $rv = array();

  $key = array_shift($params);
  $i=count($params);

  while( 0 < $i-- ) {
    array_unshift($rv, $params[$i][ $key % count($params[$i]) ]);
    $key = (int)($key / count($params[$i]));
  }
  return $rv;
}

for($i=0; $i<8; $i++) {
  $a = foo($i, $array1, $array2, $array3);
  echo join(', ', $a), "\n";
}

您可以使用这个方法来实现例如 IteratorSeekableIterator 或者甚至是ArrayAccess(与递归解决方案相比,控制被颠倒,几乎像 Python 或 Ruby 中的 yield)。
<?php
$array1 = array('dog', 'cat', 'mouse', 'bird');
$array2 = array('food', 'tooth', 'brush', 'paste');
$array3 = array('car', 'bike', 'plane', 'shuttlecraft');
$f = new Foo($array1, $array2, $array3);
foreach($f as $e) {
  echo join(', ', $e), "\n";
}

class Foo implements Iterator {
  protected $data = null;
  protected $limit = null;
  protected $current = null;

  public function __construct(/* ... */ ) {  
    $params = func_get_args();
    // add parameter arrays in reverse order so we can use foreach() in current()
    // could use array_reverse(), but you might want to check is_array() for each element.
    $this->data = array();
    foreach($params as $p) {
      // <-- add: test is_array() for each $p  -->
      array_unshift($this->data, $p);
    }
    $this->current = 0;
    // there are |arr1|*|arr2|...*|arrN| elements in the result set
    $this->limit = array_product(array_map('count', $params));
  }

  public  function current() {
    /* this works like a baseX->baseY converter (e.g. dechex() )
       the only difference is that each "position" has its own number of elements/"digits"
    */
    // <-- add: test this->valid() -->
    $rv = array();
    $key = $this->current;
    foreach( $this->data as $e) {
      array_unshift( $rv, $e[$key % count($e)] );
      $key = (int)($key/count($e));
    }
    return $rv;
  }

  public function key() { return $this->current;  }
  public function next() { ++$this->current; }
  public function rewind () { $this->current = 0; }
  public function valid () { return $this->current < $this->limit; }
}

打印
dog, food, car
dog, food, bike
dog, food, plane
dog, food, shuttlecraft
dog, tooth, car
dog, tooth, bike
[...]
bird, paste, bike
bird, paste, plane
bird, paste, shuttlecraft

(这个序列似乎没问题;-))

2
我还没有在大型词库上进行测试,但是对于中等大小的词库,它的速度相当快,而且不使用递归,我认为(如果我错了,请纠正我)可能会导致内存限制问题:
$lines = array('');

foreach ($arrays as $array) {

  $old_lines = $lines;
  $lines = array();

  foreach ($array as $word) {

    foreach ($old_lines as $line) {

      $lines[] = trim($line .' '. $word);

    } // foreach

  } // foreach

} // foreach

我猜测内存限制是由于结果数组过大所致,这在你的方法中也是一样的。但打印该行应该不会有问题。我的意思是,数组中有100^5个元素,这很多啊 ;) - Felix Kling
在较小的数组上运行得非常好,但在大数组上表现不佳。尽管如此,我还是喜欢它! - hookedonwinter

2

我的看法

class Combinator
{
     protected $words;
     protected $combinator;

     public function __construct($words, $combinator = null)
     {
         $this->words = $words;
         $this->combinator = $combinator;
     }

     public function run($combo = '')
     {
         foreach($this->words as $word) {
             if($this->combinator !== null) {
                 $this->combinator->run("$combo $word"); 
             } else {
                 echo "$combo $word", PHP_EOL;
             }
         }
     }
}

$c = new Combinator(array('dog', 'cat'), 
                    new Combinator(array('food', 'tooth'),
                                   new Combinator(array('car', 'bike'))));

$c->run();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接