给定输入,显示图像的标签分配,如下所示(从php:// stdin逐行读取,因为输入可能变得相当大)
image_a tag_lorem
image_a tag_ipsum
image_a tag_amit
image_b tag_sit
image_b tag_dolor
image_b tag_ipsum
... (there are more lines, may get up to a million)
输入的输出如下所示。基本上它与另一个条目的格式相同,表示图像标签组合是否存在于输入中。请注意,对于每个图像,它将列出所有可用标记,并通过在每行末尾使用1/0来显示是否将标记分配给图像。
image_a tag_sit 0
image_a tag_lorem 1
image_a tag_dolor 0
image_a tag_ipsum 1
image_a tag_amit 1
image_b tag_sit 1
image_b tag_lorem 0
image_b tag_dolor 1
image_b tag_ipsum 1
image_b tag_amit 0
... (more)
我已经在那里发布了我那不那么有效的解决方案。为了更好地了解输入和输出,我通过stdin将745行(这解释了10个图像的标记分配)输入到脚本中,并且在执行脚本后使用大约0.4MB的内存接收555025行。但是,由于磁盘I / O活动繁重(写入/读取临时列缓存文件时),它可能会更快地终止硬盘。
还有其他办法吗?我有另一个脚本可以将stdin转换成这样的东西(不确定这是否有用)
image_foo tag_lorem tag_ipsum tag_amit
image_bar tag_sit tag_dolor tag_ipsum
p / s:tag_ *的顺序并不重要,但它必须对所有行都相同,即这不是我想要的(注意tag_ *的顺序对于tag_a和tag_b都是不一致的)
image_foo tag_lorem 1
image_foo tag_ipsum 1
image_foo tag_dolor 0
image_foo tag_sit 0
image_foo tag_amit 1
image_bar tag_sit 1
image_bar tag_lorem 0
image_bar tag_dolor 1
image_bar tag_ipsum 1
image_bar tag_amit 0
p / s2:直到我读完标准时才知道tag_ *的范围
p / s3:我不明白为什么我要投票,如果需要澄清我很乐意提供他们,我不是想取笑或在这里发布废话。我再次重写了这个问题,使它听起来更像是一个真正的问题(?)。但是,该脚本实际上并不关心输入到底是什么或者是否使用了数据库(好吧,如果你必须知道,则从RDF数据存储中检索数据)因为我希望脚本可用于其他类型数据只要输入格式正确(因此这个问题的原始版本非常普遍)。p / s4:我试图避免使用数组,因为我想尽可能避免内存不足错误(如果745行只显示10个图像将扩展到550k行,想象一下我有100,1000,甚至10000多张图片。)
p / s5:如果您有其他语言的答案,请随时在此处发布。我曾想过用clojure解决这个问题,但仍然找不到合适的方法。
答案 0 :(得分:1)
抱歉,maby我误解了你 - 这看起来太容易了:
$stdin = fopen('php://stdin', 'r');
$columns_arr=array();
$rows_arr=array();
function set_empty_vals(&$value,$key,$columns_arr) {
$value=array_merge($columns_arr,$value);
ksort($value);
foreach($value AS $val_name => $flag) {
echo $key.' '.$val_name.' '.$flag.PHP_EOL;
}
$value=NULL;
}
while ($line = fgets($stdin)) {
$line=trim($line);
list($row,$column)=explode(' ',$line);
$row=trim($row);
$colum=trim($column);
if(!isset($rows_arr[$row]))
$rows_arr[$row]=array();
$rows_arr[$row][$column]=1;
$columns_arr[$column]=0;
}
array_walk($rows_arr,'set_empty_vals',$columns_arr);
UPD:
PHP容易100万行:
$columns_arr = array();
$rows_arr = array();
function set_null_arr(&$value, $key, $columns_arr) {
$value = array_merge($columns_arr, $value);
ksort($value);
foreach($value AS $val_name => $flag) {
//echo $key.' '.$val_name.' '.$flag.PHP_EOL;
}
$value=NULL;
}
for ($i = 0; $i < 100000; $i++) {
for ($j = 0; $j < 10; $j++) {
$row='row_foo'.$i;
$column='column_ipsum'.$j;
if (!isset($rows_arr[$row]))
$rows_arr[$row] = array();
$rows_arr[$row][$column] = 1;
$columns_arr[$column] = 0;
}
}
array_walk($rows_arr, 'set_null_arr', $columns_arr);
echo memory_get_peak_usage();
对我来说是147Mb。
Last UPD - 这就是我看到内存使用率低(但速度很快)的原因:
//Approximate stdin buffer size, 1Mb should be good
define('MY_STDIN_READ_BUFF_LEN', 1048576);
//Approximate tmpfile buffer size, 1Mb should be good
define('MY_TMPFILE_READ_BUFF_LEN', 1048576);
//Custom stdin line delimiter(\r\n, \n, \r etc.)
define('MY_STDIN_LINE_DELIM', PHP_EOL);
//Custom stmfile line delimiter - chose smallset possible
define('MY_TMPFILE_LINE_DELIM', "\n");
//Custom stmfile line delimiter - chose smallset possible
define('MY_OUTPUT_LINE_DELIM', "\n");
function my_output_arr($field_name,$columns_data) {
ksort($columns_data);
foreach($columns_data AS $column_name => $column_flag) {
echo $field_name.' '.$column_name.' '.$column_flag.MY_OUTPUT_LINE_DELIM;
}
}
$tmpfile=tmpfile() OR die('Can\'t create/open temporary file!');
$buffer_len = 0;
$buffer='';
//I don't think there is a point to save columns array in file -
//it should be small enough to hold in memory.
$columns_array=array();
//Open stdin for reading
$stdin = fopen('php://stdin', 'r') OR die('Failed to open stdin!');
//Main stdin reading and tmp file writing loop
//Using fread + explode + big buffer showed great performance boost
//in comparison with fgets();
while ($read_buffer = fread($stdin, MY_STDIN_READ_BUFF_LEN)) {
$lines_arr=explode(MY_STDIN_LINE_DELIM,$buffer.$read_buffer);
$read_buffer='';
$lines_arr_size=count($lines_arr)-1;
$buffer=$lines_arr[$lines_arr_size];
for($i=0;$i<$lines_arr_size;$i++) {
$line=trim($lines_arr[$i]);
//There must be a space in each line - we break in it
if(!strpos($line,' '))
continue;
list($row,$column)=explode(' ',$line,2);
$columns_array[$column]=0;
//Save line in temporary file
fwrite($tmpfile,$row.' '.$column.MY_TMPFILE_LINE_DELIM);
}
}
fseek($tmpfile,0);
$cur_row=NULL;
$row_data=array();
while ($read_buffer = fread($tmpfile, MY_TMPFILE_READ_BUFF_LEN)) {
$lines_arr=explode(MY_TMPFILE_LINE_DELIM,$buffer.$read_buffer);
$read_buffer='';
$lines_arr_size=count($lines_arr)-1;
$buffer=$lines_arr[$lines_arr_size];
for($i=0;$i<$lines_arr_size;$i++) {
list($row,$column)=explode(' ',$lines_arr[$i],2);
if($row!==$cur_row) {
//Output array
if($cur_row!==NULL)
my_output_arr($cur_row,array_merge($columns_array,$row_data));
$cur_row=$row;
$row_data=array();
}
$row_data[$column]=1;
}
}
if(count($row_data)&&$cur_row!==NULL) {
my_output_arr($cur_row,array_merge($columns_array,$row_data));
}
答案 1 :(得分:1)
这是一个适用于您提供的测试数据的MySQL示例:
CREATE TABLE `url` (
`url1` varchar(255) DEFAULT NULL,
`url2` varchar(255) DEFAULT NULL,
KEY `url1` (`url1`),
KEY `url2` (`url2`)
);
INSERT INTO url (url1, url2) VALUES
('image_a', 'tag_lorem'),
('image_a', 'tag_ipsum'),
('image_a', 'tag_amit'),
('image_b', 'tag_sit'),
('image_b', 'tag_dolor'),
('image_b', 'tag_ipsum');
SELECT url1, url2, assigned FROM (
SELECT t1.url1, t1.url2, 1 AS assigned
FROM url t1
UNION
SELECT t1.url1, t2.url2, 0 AS assigned
FROM url t1
JOIN url t2
ON t1.url1 != t2.url1
JOIN url t3
ON t1.url1 != t3.url1
AND t1.url2 = t3.url2
AND t2.url2 != t3.url2 ) tmp
ORDER BY url1, url2;
结果:
+---------+-----------+----------+
| url1 | url2 | assigned |
+---------+-----------+----------+
| image_a | tag_amit | 1 |
| image_a | tag_dolor | 0 |
| image_a | tag_ipsum | 1 |
| image_a | tag_lorem | 1 |
| image_a | tag_sit | 0 |
| image_b | tag_amit | 0 |
| image_b | tag_dolor | 1 |
| image_b | tag_ipsum | 1 |
| image_b | tag_lorem | 0 |
| image_b | tag_sit | 1 |
+---------+-----------+----------+
这应该很简单,可以转换为SQLite,因此如果需要,您可以使用PHP将数据读入临时SQLite数据库,然后提取结果。
答案 2 :(得分:0)
将输入数据放入数组中,然后使用usort对它们进行排序,定义比较函数,按行值比较数组元素,如果行值相等,则比较列值。
答案 3 :(得分:0)
这是我当前的实现,我不喜欢它,但它现在可以完成这项工作。
#!/usr/bin/env php
<?php
define('CACHE_MATCH', 0);
define('CACHE_COLUMN', 1);
define('INPUT_ROW', 0);
define('INPUT_COLUMN', 1);
define('INPUT_COUNT', 2);
output_expanded_entries(
cache_input(array(tmpfile(), tmpfile()), STDIN, fgets(STDIN))
);
echo memory_get_peak_usage();
function cache_input(Array $cache_files, $input_pointer, $input) {
if(count($cache_files) != 2) {
throw new Exception('$cache_files requires 2 file pointers');
}
if(feof($input_pointer) == FALSE) {
cache_match($cache_files[CACHE_MATCH], trim($input));
cache_column($cache_files[CACHE_COLUMN], process_line($input));
cache_input(
$cache_files,
$input_pointer,
fgets($input_pointer)
);
}
return $cache_files;
}
function cache_column($cache_column, $input) {
if(empty($input) === FALSE) {
rewind($cache_column);
$column = get_field($input, INPUT_COLUMN);
if(column_cached_in_memory($column) === FALSE && column_cached_in_file($cache_column, fgets($cache_column), $column) === FALSE) {
fputs($cache_column, $column . PHP_EOL);
}
}
}
function cache_match($cache_match, $input) {
if(empty($input) === FALSE) {
fputs($cache_match, $input . PHP_EOL);
}
}
function column_cached_in_file($cache_column, $current, $column, $result = FALSE) {
return $result === FALSE && feof($cache_column) === FALSE ?
column_cached_in_file($cache_column, fgets($cache_column), $column, $column == $current)
: $result;
}
function column_cached_in_memory($column) {
static $local_cache = array(), $index = 0, $count = 500;
$result = TRUE;
if(in_array($column, $local_cache) === FALSE) {
$result = FALSE;
$local_cache[$index++ % $count] = $column;
}
return $result;
}
function output_expanded_entries(Array $cache_files) {
array_map('rewind', $cache_files);
for($current_row = NULL, $cache = array(); feof($cache_files[CACHE_MATCH]) === FALSE;) {
$input = process_line(fgets($cache_files[CACHE_MATCH]));
if(empty($input) === FALSE) {
if($current_row !== get_field($input, INPUT_ROW)) {
output_cache($current_row, $cache);
$cache = read_columns($cache_files[CACHE_COLUMN]);
$current_row = get_field($input, INPUT_ROW);
}
$cache = array_merge(
$cache,
array(get_field($input, INPUT_COLUMN) => get_field($input, INPUT_COUNT))
);
}
}
output_cache($current_row, $cache);
}
function output_cache($row, $column_count_list) {
if(count($column_count_list) != 0) {
printf(
'%s %s %s%s',
$row,
key(array_slice($column_count_list, 0, 1)),
current(array_slice($column_count_list, 0, 1)),
PHP_EOL
);
output_cache($row, array_slice($column_count_list, 1));
}
}
function get_field(Array $input, $field) {
$result = NULL;
if(in_array($field, array_keys($input))) {
$result = $input[$field];
} elseif($field == INPUT_COUNT) {
$result = 1;
}
return $result;
}
function process_line($input) {
$result = trim($input);
return empty($result) === FALSE && strpos($result, ' ') !== FALSE ?
explode(' ', $result)
: NULL;
}
function push_column($input, Array $result) {
return empty($input) === FALSE && is_array($input) ?
array_merge(
$result,
array(get_field($input, INPUT_COLUMN))
)
: $result;
}
function read_columns($cache_columns) {
rewind($cache_columns);
$result = array();
while(feof($cache_columns) === FALSE) {
$column = trim(fgets($cache_columns));
if(empty($column) === FALSE) {
$result[$column] = 0;
}
}
return $result;
}
编辑:昨天的版本被窃听:/