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?

谢谢!

4

4 回答 4

9

您可以将所有单词数组放入一个数组并使用如下递归函数:

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

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

 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
于 2010-02-11T18:25:01.040 回答
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个元素的函数。由于您只需要一个整数来存储当前状态,因此当您拥有许多/大型数组时,内存消耗不会“爆炸”。正如克里斯在他的评论中所说,您可以用速度(使用较小的集合时)换取低内存消耗。(虽然我认为 - 实现 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(从而与递归解决方案相比,反转控制,几乎就像yield在 python 或 ruby​​ 中的 a )

<?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

(顺序似乎没问题;-))

于 2010-02-12T07:21:30.400 回答
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
于 2010-02-11T19:01:37.843 回答
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();
于 2010-02-11T21:18:20.840 回答