воскресенье, 9 ноября 2014 г.

Великий 2011-ый год

Чудесно! Мой первый проект попал в веб-архив:

http://web.archive.org/web/20110124180220/http://idobi.ru/?

О Великое Дао! Код тоже сохранился:

http://web.archive.org/web/20110127015324/http://idobi.ru/index.php.txt

Мама мия! Даже корневая формочка тут.


И переписка на w3.org!

http://lists.w3.org/Archives/Public/site-comments/2011Jan/0027.html

Stop the web of texts! Start a web of things!
This is the main idea of this site. Here you'll find a solution to the majour problem of the internet today. The flood of information has grown to an overwhelming amount. Search for "black cat" in Google produces 80 000 000 pages. When you ask a more specific question like "black cat which lives in London and likes milk" you get rubbish. You get pages that contain all the words:
It is a myth that cats like to be alone the majority of the time. ... If your cat loves milk, it is ok to spoil him with a few sips now and again, ... They are currently living in a "cat safe" room that is completely black at night. ... Abandoned kittens in London rescued! A tale of two kitties by LondonGirl ...
Perhaps it were a better result when you'd get as an answer "We have no information on such cats".
The reason for search failures is that the computer can't understand to which object a word is related to. For example, when you talk about a thing and give it different names. "I like summer and warm weather. I hate winter when it's cold. This story started in my favorite time of year." How can a computer guess that "time" is related to "summer"? There is no way. Computers can't understand human texts because they don't possess human experience.
So the solution is obvious. We must give to computers information about things that come up in our texts. Then they can have a chance to get the message. Ontologies and meta tags are popular now. The only problem is no one wants to give additional efforts explaining to machines what is meant. It is sufficient that humans understand.
We suggest to change the approach to thinking of the internet as a collection of texts. Instead we must view the web as a collection of things. A node in the web is an aspect of a single object. "Cat" is a node and "black" is a node. These two nodes are connected. And they relate to one single cat. Real or imaginary. It is not a general notion of a cat or blackness. In this case the web becomes a semantic database, that can manage search requests like "black cat which lives in London and likes milk".
More articles will follow soon. If you want to dive in now turn on the node panel. You can view the PHP code in file index.php.txt.
http://youtu.be/zug4mkHRt9A



<?php
// This code requires PHP 5.3 because it uses late static binding.
// UTF-8 without BOM
class node {

 protected $id; // string
 protected $parents; // collection
 protected $children; // collection
 
 protected $creation_time;
 protected $last_update_time;
 
 protected $to_be_updated; // boolean
 
 private static $count; // integer
 private static $cache = array(); // array

 public function __construct ($id=null) {
  $this->id = static::compose_id($id);
  $this->parents = collection::construct();
  $this->children = collection::construct();
  $this->creation_time = date('Y-m-d H:i:s');
  $this->last_update_time = date('Y-m-d H:i:s');
  $this->to_be_updated = false;
 }
 public static function exists ($id) {
  if (is_string($id) && strlen($id) > 0) {
   $path = static::path($id);
   return file_exists($path);
  }else{
   exit('Invalid '.static::class_name().' ID: '.var_dump($id));
  }
 }
 public static function construct ($id=null) {
  
  $instance = null;
  
  // try to read from file
  if (!is_null($id) && static::exists($id)) {
   $instance = static::read ($id);
  }
  
  
  // create if allowed
  if (is_null($instance) && true) {
   $instance = static::create($id);
  }

  return $instance;
 }
 public function __destruct () {
  if ($this->to_be_updated) {
   $this->to_be_updated = false;
   static::update($this);
   $this->last_update_time = date('Y-m-d H:i:s');
  }
 }
 
 public function must_be_updated ($decision=true) {
  $this->to_be_updated = $decision;
  return $this;
 }
 
 public function id () {
  return $this->id;
 }
 public function parents () {
  return $this->parents;
 }
 public function children () {
  return $this->children;
 }
 
 public function creation_time () {
  return $this->creation_time;
 }
 public function last_update_time () {
  return $this->last_update_time;
 }
 
 public static function remove ($id) {
  $path = static::path($id);
  if (file_exists($path)) {
   unlink($path);
   rmdir(dirname($path));
  }
 }
 
 public static function count () {
  return self::$count;
 }
 
 protected static function create ($id=null) {
  $class = static::class_name();
  $instance = new $class ($id); // object properties are initialized by __construct()
  self::$count++;
  return $instance;
 }
 protected static function read ($id) {
  if (isset(self::$cache[$id])) { // get node from cache
   return self::$cache[$id];
  }
  
  $instance = null;
  if (static::exists($id)) {
   $instance = unserialize(base64_decode(file_get_contents(static::path($id))));
   if (!(is_object($instance) && is_a($instance, __CLASS__))) {
    $instance = null;
   }else{ // put node to cache 
    self::$cache[$id] = $instance;
   }
  }
  self::$count++;
  return $instance;
 }
 protected static function update (node $instance) {
  if (isset(self::$cache[$instance->id()])) { // remove from cache
   unset(self::$cache[$instance->id()]);
  }
  
  $path = static::path($instance->id());
  static::make_directory($path);
  $bytes = file_put_contents($path, base64_encode(serialize($instance)));
  if ($bytes === false) {
   exit('A '.__CLASS__.' object could not be updated.');
  }
 }
 protected static function delete (node $instance) {
  unlink(static::path($instance->id()));
 }
  
 protected static function compose_id ($inspiration=null) {
  if (!is_null($inspiration) && !static::exists($inspiration)) {
   $id = $inspiration;
  }else{
   do {
    $id = '_'.md5($_SERVER['SERVER_NAME'].time().rand(1, 1000000).var_dump($inspiration));
   } while(file_exists(dirname(static::path($id))));
  }
  return $id;
 }
 protected static function class_name () { // overload this function in child classes
  return __CLASS__;
 }
 protected static function class_directory () {
  return static::class_name();
 }
 protected static function class_file () {
  return static::class_name().'.txt';
 }
 protected static function path ($id) {
  $ds = static::directory_separator();
  $path = rtrim(dirname(__FILE__), $ds).$ds.static::class_directory().$ds.$id.$ds.static::class_file();
  return $path;
 }
 protected static function make_directory ($path) { // secure that the given directory exists and return transformed path
  $ds = static::directory_separator();
  $current_directory = rtrim(dirname(__FILE__), $ds);
  $short_path = str_replace($current_directory.$ds, '', $path);
  $path_array = explode($ds, $short_path);
  $file_name = array_pop($path_array);
  foreach ($path_array as $directory) {
   $current_directory .= $ds.$directory;
   if (!file_exists($current_directory) || !is_dir($current_directory)) {
    $ok = mkdir($current_directory, 0777);
    if (!$ok) {
     exit('Folder '.$current_directory.' could not be created.');
    }
   }
  }
 }
 protected static function directory_separator () {
  if (strpos(__FILE__, '\\')) {
   $directory_separator = '\\';
  }elseif(strpos(__FILE__, '/')) {
   $directory_separator = '/';
  }else{
   $directory_separator = '/';
  }
  return $directory_separator;
 }

}
class word extends node {

 protected $entities; // collection
 protected $view; // string
 
 public function __construct ($id=null) {
  parent::__construct($id);
  $this->entities = collection::construct();
  $this->view = '';
 }

 public function entities () {
  return $this->entities;
 }

 public function ponder (entity $entity) {
  
  // update ids
  $this->entities->add($entity->id());
  
  // update parent word list
  foreach ($entity->parents()->to_array() as $id) {
   $parent_entity = entity::construct($id);
   $this->parents()->add($parent_entity->name());
  }
  
  // update child word list
  foreach ($entity->children()->to_array() as $id) {
   $child_entity = entity::construct($id);
   $this->children()->add($child_entity->name());
  }
  
  return $this;
  
 }

 public function recollect () {
 
  $this->to_be_updated = true;
 
  $children = collection::construct();
  $parents = collection::construct();
  
  foreach ($this->entities->to_array() as $id) {
   if (entity::exists($id)) {
    
    $here = entity::construct($id);
    
    // check word
    if ($here->name() != $this->id) {
     $this->entities->remove($id); // entity has other name
     continue;
    }
    
    // collect children
    foreach ($here->children->to_array() as $there_id) {
     if (entity::exists($there_id)) {
      $there = entity::construct($there_id);
      if ($there->parents()->exists($here->id())) {
       $children->add($there->name());
      }else{
       $there->parents()->add($here->id()); // repare connection
      }
     }
    }

    // collect parents
    foreach ($here->parents->to_array() as $there_id) {
     if (entity::exists($there_id)) {
      $there = entity::construct($there_id);
      if ($there->children()->exists($here->id())) {
       $parents->add($there->name());
      }else{
       $there->children()->add($here->id()); // repare connection
      }
     }
    }

   }else{
    $this->entities->remove($id); // entity absent
   }
   
  }
  
  $this->children = $children;
  $this->parents = $parents;
 
 }
 
 public function set_view ($view) {
  $this->view = $view;
  return $this;
 }
 public function view () {
  return $this->view;
 }

 public static function dictionary () {
  $dictionary = array();
  
  $ds = static::directory_separator();
  $path = rtrim(dirname(__FILE__), $ds).$ds.static::class_directory().$ds;
  $paths = glob($path.'*', GLOB_ONLYDIR);
  
  foreach ($paths as $path) {
   $dictionary[] = iconv(static::file_system_encoding(), static::php_encoding(), basename($path));
  }
  
  return $dictionary;
 }
 
 protected static function class_name () { 
  return __CLASS__;
 }
 protected static function path ($id) {
  $ds = static::directory_separator();
  $id = iconv(static::php_encoding(), static::file_system_encoding(), $id);
  $path = rtrim(dirname(__FILE__), $ds).$ds.static::class_directory().$ds.$id.$ds.static::class_file();
  return $path;
 }
 protected static function php_encoding () {
  return 'UTF-8';
 }
 protected static function file_system_encoding () {
  return 'Windows-1251';
 }

 
}
class entity extends node {
 
 const USE_DEFAULT_VIEW = 'USE_DEFAULT_VIEW';
 
 protected $name; // string
 protected $view; // string
 protected $owner; // string
 
 
 public function __construct ($id=null) {
  parent::__construct($id);
  $this->name = '';
  $this->view = self::USE_DEFAULT_VIEW;
  $this->owner = '';
 }
 public function __destruct () {

  if ($this->to_be_updated) {
   if ($this->name != '') {
    word::construct($this->name)->must_be_updated()->ponder($this);
   }
  }
  
  parent::__destruct ();
 }

 
 public function set_name ($name) {
  $this->name = $name;
  $word = word::construct($name);
  return $this;
 }
 public function name () {
  return $this->name;
 }
 public function set_view ($view) {
  $this->view = $view;
  return $this;
 }
 public function view () {
  $view = $this->view;
  return $view;
 }
 public function owner () {
  return $this->owner;
 }

 public function display ($arguments=null) {
  
  // prevent displaying
  if (isset($_GET['display']) && in_array($_GET['display'], array('0', 'off', 'out'))) {
   return __FILE__.': '.__FUNCTION__;
  }
  
  // display
  $q = query::construct();
  $e =& $this;
  $view = $this->view();
  if ($view == self::USE_DEFAULT_VIEW) {
   $view = word::construct($this->name)->view();
  }
  ob_start();
  eval(' ?>' . $view . '<?php ');
  $html = ob_get_contents();
  ob_end_clean();
  //$html = iconv('UTF-8', 'Windows-1251', $html);
  
  return $html;
  
 }
 
 public static function compose_owner ($email, $encripted_password) {
  return $email.':'.$encripted_password;
 }
 public function change_possible ($owner) {
  if ($owner == $this->owner || $this->owner == '') {
   return true;
  }else{
   return false;
  }
 }
 public function take_ownership ($owner) {
  if ($this->change_possible($owner)) {
   $this->owner = $owner;
  }
  return $this;
 }
 public function drop_ownership ($owner) {
  if ($this->change_possible($owner)) {
   $this->owner = '';
  }
  return $this;
 }
 public function give_ownership ($old_owner, $new_owner) {
  if ($this->change_possible($old_owner)) {
   $this->owner = $new_owner;
  }
  return $this;
 }
 
 public static function add_child ($name, $parent, $view = entity::USE_DEFAULT_VIEW) {
  
  // create
  $child = entity::construct()->set_name($name)->set_view($view);
  $child_id = $child->id();
  // bind
  $parent->children()->add($child_id);
  $child->parents()->add($parent->id());
  // update
  $child->must_be_updated()->take_ownership($parent->owner())->__destruct();
  $child = entity::construct($child_id);
  $parent->must_be_updated();
  
  return $child;
 } 
 public static function add_parent ($name, $child, $view = entity::USE_DEFAULT_VIEW) {
  
  // create
  $parent = entity::construct()->set_name($name)->set_view($view);
  $parent_id = $parent->id();
  // bind
  $parent->children()->add($child->id());
  $child->parents()->add($parent_id);
  // update
  $parent->must_be_updated()->take_ownership($child->owner())->__destruct();
  $parent = entity::construct($parent_id);
  $child->must_be_updated();
  
  return $parent;
 } 
 public static function remove_child ($name, $parent) {
  
 }
 public static function remove_parent ($name, $child) {
  
 }
 public static function check_parent ($parent_name, $child) {
  if ($child->parents()->count() !== 1) return false;
  $parent_id = array_pop($child->parents()->to_array());
  if (!entity::exists($parent_id)) return false;
  $parent = entity::construct($parent_id);
  if ($parent->name() != $parent_name) return false;
  return $parent;
 }
 
 
 protected static function class_name () { // use static::class_name() to call this function
  return __CLASS__;
 }

}
class collection {

 private $collection;

 public static function construct () {
  $class = __CLASS__;
  $instance = new $class ();
  $instance->collection = array();
  return $instance;
 }
 
 public function add ($value) {
  if (!$this->exists($value)) {
   $this->collection[] = $value;
  }
 }
 public function remove ($value) {
  $key = $this->key($value);
  if ($key !== false) {
   unset($this->collection[$key]);
   $this->collection = array_values($this->collection); // fill the gap
  }
 }
 
 public function exists ($value) {
  $key = $this->key($value);
  if ($key !== false) {
   return true;
  }else{
   return false;
  }
 }
 public function count () {
  return count($this->collection);
 }
 
 public function up ($value) {
  $key = $this->key($value);
  if ($key === 0) {
   array_shift($this->collection);
   $this->collection[] = $value;
  }else{
   $this->collection[$key] = $this->collection[$key-1];
   $this->collection[$key-1] = $value;
  }
  return $this;
 }
 public function down ($value) {
  $key = $this->key($value);
  if ($key === (count($this->collection)-1)) {
   array_pop($this->collection);
   array_unshift($this->collection, $value);
  }else{
   $this->collection[$key] = $this->collection[$key+1];
   $this->collection[$key+1] = $value;
  }
  return $this;
 }
 
 public function to_array () {
  return $this->collection;
 }
 public function to_string ($separator=',') {
  return implode($separator, $this->collection);
 }
 
 private function key ($value) {
  return array_search($value, $this->collection);
 }
 
}
class query {

 protected $name; // string
 protected $selection; // collection

 protected static $script_start_time;
 
 protected function __construct ($name=null) {
  $this->name = '';   
  $this->selection = collection::construct();
  
  if (!is_null($name)) {
   if (is_object($name)) {
    if (is_a($name, 'query')) {
     $this->name = $name->name();
     $this->selection = $name->to_collection();
    }elseif (is_a($name, 'entity')) {
     $this->name = $name->name();
     $this->selection->add($name->id());
    }elseif (is_a($name, 'word')) {
     $this->name = $name->id();
     $this->selection = $name->entities();
    }
   }elseif (word::exists($name)) {
    $this->name = $name;   
    $this->selection = word::construct($name)->entities();
   }elseif (entity::exists($name)) {
    $anchor = entity::construct($name);
    $this->name = $anchor->name();
    $this->selection->add($anchor->id());
   }
  }
  
 }
 public static function construct ($name=null) {
  $class = __CLASS__;
  $instance = new $class ($name); 
  return $instance;
 }

 public function name () {
  return $this->name;
 }
 
 public function get ($name=null) {
  $instance = static::construct($name);
  return $instance;
 }
 
 public function with ($description) { // filter
 
  if ($this->name == '' || $this->selection->count() == 0) return $this; // skip in empty objects
 
  list($delimitors, $names) = static::delimitors_and_names($description);
  foreach ($this->selection->to_array() as $entity_id) {
   if (entity::exists($entity_id)) {
    $current_entities = array(entity::construct($entity_id));
    foreach ($names as $index => $name) {
     $results = array();
     $delimitor = $delimitors[$index];
     if ($delimitor == ':') { // leads to parents
      foreach ($current_entities as $current_entity) {
       $this->find_parents($name, $current_entity, $results);
       if (count($results) == 0) { // decide
        $this->selection->remove($entity_id);
       }
      }
     }elseif ($delimitor == '.') { // leads to children
      foreach ($current_entities as $current_entity) {
       $this->find_children($name, $current_entity, $results);
       if (count($results) == 0) { // decide
        $this->selection->remove($entity_id);
       }
      }
     }elseif ($delimitor == '=') { // leads to view
      foreach ($current_entities as $current_entity) {
       if (isset($name[0])) {
        $operation = $name[0];
        switch ($operation) {
         case '*':
          $operand = substr($name, 1);
          if (strpos($current_entity->view(), $operand) !== false) {
           $results[] = $current_entity;
          }else{
           $this->selection->remove($entity_id);
          }
          break;
         case '^':
          $operand = substr($name, 1);
          if (strpos($current_entity->view(), $operand) === 0) {
           $results[] = $current_entity;
          }else{
           $this->selection->remove($entity_id);
          }
          break;
         case '$':
          $operand = substr($name, 1);
          if (strpos(strrev($current_entity->view()), strrev($operand)) === 0) {
           $results[] = $current_entity;
          }else{
           $this->selection->remove($entity_id);
          }
          break;
         default:
          $operand = $name;
          if ($current_entity->view() == $operand) {
           $results[] = $current_entity;
          }else{
           $this->selection->remove($entity_id);
          }
          break;
        }
       }
      }
     }
     $current_entities = $results;
    }
   }
  }
  
  return $this;
 }
 public function without ($description) { // filter
 
  if ($this->name == '' || $this->selection->count() == 0) return $this; // skip in empty objects
 
  list($delimitors, $names) = static::delimitors_and_names($description);
  foreach ($this->selection->to_array() as $entity_id) {
   if (entity::exists($entity_id)) {
    $current_entities = array(entity::construct($entity_id));
    foreach ($names as $index => $name) {
     $results = array();
     $delimitor = $delimitors[$index];
     if ($delimitor == ':') { // leads to parents
      foreach ($current_entities as $current_entity) {
       $this->find_parents($name, $current_entity, $results);
       if (count($results) > 0) { // decide
        $this->selection->remove($entity_id);
       }
      }
     }elseif ($delimitor == '.') { // leads to children
      foreach ($current_entities as $current_entity) {
       $this->find_children($name, $current_entity, $results);
       if (count($results) > 0) { // decide
        $this->selection->remove($entity_id);
       }
      }
     }
     $current_entities = $results;
    }
   }
  }
  
  return $this;
 }
 public function find ($description) { // collect amongst children (.) or parents (:)
  
  $found_entities = collection::construct();
  
  if ($this->name == '' || $this->selection->count() == 0) return $this; // skip in empty objects
 
  list($delimitors, $names) = static::delimitors_and_names($description);
  foreach ($this->selection->to_array() as $entity_id) {
   if (entity::exists($entity_id)) {
    $current_entities = array(entity::construct($entity_id));
    $results = array();
    foreach ($names as $index => $name) {
     $results = array();
     $delimitor = $delimitors[$index];
     if ($delimitor == ':') { // leads to parents
      foreach ($current_entities as $current_entity) {
       $this->find_parents($name, $current_entity, $results);
      }
     }elseif ($delimitor == '.') { // leads to children
      foreach ($current_entities as $current_entity) {
       $this->find_children($name, $current_entity, $results);
      }
     }
     $current_entities = $results;
    }
    $this->name = $name;
    foreach ($results as $entity) {
     $found_entities->add($entity->id());
    }
   }
  }
  
  $this->selection = $found_entities;
  
  return $this;
 }
 
 public function parents () { 
  
  if ($this->name == '' || $this->selection->count() == 0) return $this; // skip in empty objects

  $parents = collection::construct();
  foreach ($this->selection->to_array() as $entity_id) {
   if (entity::exists($entity_id)) { // avoid creating new entities
    foreach ($entity->parents()->to_array() as $parent_id) {
     $parents->add($parent_id);
    }
   }
  }
  $this->selection = $parents;
  
  return $this;
  
 }
 public function children () {
 
  if ($this->name == '' || $this->selection->count() == 0) return $this; // skip in empty objects

  $children = collection::construct();
  foreach ($this->selection->to_array() as $entity_id) {
   if (entity::exists($entity_id)) { // avoid creating new entities
    $entity = entity::construct($entity_id);
    foreach ($entity->children()->to_array() as $child_id) {
     $children->add($child_id);
    }
   }
  }
  $this->selection = $children;
  
  return $this;
  
 }
 
 public function split () {
  $split_array = array();
  foreach ($this->selection->to_array() as $id) {
   if (entity::exists($id)) {
    $split_array[] = $this->get($id);
   }
  }
  return $split_array;
 }
 
 public function to_views ($arguments=null) {
  $views = array();
  foreach ($this->selection->to_array() as $id) {
   if (entity::exists($id)) {
    $views[] = entity::construct($id)->display($arguments);
   }
  }
  return $views;
 }
 public function to_names () {
  $names = array();
  foreach ($this->selection->to_array() as $id) {
   if (entity::exists($id)) {
    $names[] = entity::construct($id)->name();
   }
  }
  return $names;
 }
 public function to_links () {
  $links = array();
  foreach ($this->selection->to_array() as $id) {
   if (entity::exists($id)) {
    $links[] = '<a href="?entity_id='.$id.'">'.entity::construct($id)->name().'</a>';
   }
  }
  return $links;
 }
 public function to_ids () {
  return $this->selection->to_array();
 }
 public function to_entities () {
  $entities = array();
  foreach ($this->selection as $id) {
   if (entity::exists($id)) {
    $entities[] = entity::construct($id);
   }
  }
  return $entities;
 }
 public function to_collection () {
  return $this->selection;
 }
 
 public function to_view ($arguments=null, $separator='', $wrapper_top='', $wrapper_bottom='') {
  $view = '';
  $views = $this->to_views($arguments);
  if (!empty($views)) {
   $view .= $wrapper_top;
   $view .= implode($separator, $views);
   $view .= $wrapper_bottom;
  }
  return $view;
 }
 public function to_menu ($separator='', $wrapper_top='', $wrapper_bottom='') {
  $menu = '';
  $links = $this->to_links();
  if (!empty($links)) {
   $menu .= $wrapper_top;
   $menu .= implode($separator, $links);
   $menu .= $wrapper_bottom;
  }
  return $menu;
 }
 
 public static function notation_to_code ($notation, $variable = '$q') {
  $code = '';
  if ($notation != '') {
   $code = $notation;
   $code = str_replace('?[', '->find(\'', $code);
   $code = str_replace('+[', '->with(\'', $code);
   $code = str_replace('-[', '->without(\'', $code);
   $code = str_replace(']', '\')', $code);
   $arrow_position = strpos($code, '->');
   if ($arrow_position !== false) {
    $word = substr($code, 0, $arrow_position);
    $code = $variable.'->get(\''.substr_replace($code, $word.'\')', 0, strlen($word)).';';
   }else{
    $code = $variable.'->get(\''.$code.'\');';
   }
  }
  return $code;
 }
 
 public static function set_script_start_time () {
  self::$script_start_time = self::time();
 }
 public static function time () {
  $mtime = microtime();
  $mtime = explode(" ",$mtime);
  $mtime = $mtime[1] + $mtime[0];
  return $mtime;
 } 
 public static function time_elapsed () {
  return self::time() - self::$script_start_time;
 }
 
 protected function find_parents ($name, $entity, &$parents=array(), &$track=array()) {

  if ($this->time_elapsed() > 10) { // s
   return;
  }
  if (memory_get_usage() > 6*1048576) { // Mb
   return;
  }
 
  if ($entity->parents()->count() == 0) { // no parents
   return;
  }
  
  foreach ($entity->parents()->to_array() as $parent_id) {
   if (entity::exists($parent_id)) {

    if (in_array($parent_id, $track)) { // avoid repetition
     continue;
    }else{
     $track[] = $parent_id;
    }
    
    $parent = entity::construct($parent_id);
    $parent_name = $parent->name();
    
    if ($parent_name == $this->name) { // avoid overlapping
     continue;
    }

    if ($parent_name == $name) { // names match
     $parents[] = $parent;
    }
    
    $this->find_parents($name, $parent, $parents, $track); // recursive function
   }
  }
  
  return;
 }
 protected function find_children ($name, $entity, &$children=array(), &$track=array()) {

  if ($this->time_elapsed() > 10) { // s
   return;
  }
  if (memory_get_usage() > 6*1048576) { // Mb
   return;
  }
 
 
  if ($entity->children()->count() == 0) { // no children
   return;
  }
  
  foreach ($entity->children()->to_array() as $child_id) {
   if (entity::exists($child_id)) {

    if (in_array($child_id, $track)) { // avoid repetition
     continue;
    }else{
     $track[] = $child_id;
    }
    
    $child = entity::construct($child_id);
    $child_name = $child->name();
    
    if ($child_name == $this->name) { // avoid overlapping
     continue;
    }

    if ($child_name == $name) { // names match
     $children[] = $child;
    }
    
    $this->find_children($name, $child, $children, $track); // recursive function
   }
  }
  
  return;
 }

 protected static function delimitors_and_names ($description) {
  preg_match_all('/(\.|\:|\=)([^\.\:\=]+)/', $description, $matches);
  $delimitors = $matches[1];
  $names = $matches[2];
  return array($delimitors, $names);
 }
}
class panel {
 
 private $entity; // entity
 private $owner; // string
 private $search_query; // string
 
 public static function construct ($id=null) {
  $class = __CLASS__;
  $instance = new $class ();
  return $instance;
 }
 
 public function set_entity (entity $entity) {
  $this->entity = $entity;
  return $this;
 }
 public function set_owner ($owner) {
  $this->owner = $owner;
  return $this;
 }
 public function set_search_query ($search_query) {
  $this->search_query = $search_query;
  return $this;
 }
 
 
 public function change_name ($name) {
  $old_name = $this->entity->name();
  
  $this->entity->set_name($name);
  $this->update_entity();
  
  $word = word::construct($old_name);
  $word->recollect();
  $word->must_be_updated()->__destruct(); // update
  
  return $this;
 }
 public function add_child ($child) {
 
  $id = trim($child, ' ');
  if (entity::exists($id)) { // id given
   $entity = entity::construct($id);
  }else{ // name given
   $entity = entity::construct()->set_name($child);
  }
  
  $this->entity->children()->add($entity->id());
  $this->update_entity();
  
  $entity
   ->take_ownership($this->entity->owner())
   ->parents()->add($this->entity->id())
  ;
  $entity->display(); // run action entity
  $entity->must_be_updated()->__destruct(); // update
  
  $word = word::construct($this->entity->name());
  $word->recollect();
  $word->must_be_updated()->__destruct(); // update
  
  return $this;
  
 }
 public function add_parent ($parent) {
  
  $id = trim($parent, ' ');
  if (entity::exists($id)) { // id given
   $entity = entity::construct($id);
   $entity = entity::construct($parent);
  }else{ // name given
   $entity = entity::construct()->set_name($parent);
  }
  
  $this->entity->parents()->add($entity->id());
  $this->update_entity();
  
  $entity
   ->take_ownership($this->entity->owner())
   ->children()->add($this->entity->id())
  ;
  $entity->must_be_updated()->__destruct(); // update
  
  $word = word::construct($this->entity->name());
  $word->recollect();
  $word->must_be_updated()->__destruct(); // update
  
  return $this;
  
 }
 public function remove_child ($id) {

  $this->entity->children()->remove($id);
  $this->update_entity();

  if (entity::exists($id)) { 
   $entity = entity::construct($id);
   $entity->parents()->remove($this->entity->id());
   $entity->must_be_updated()->__destruct(); // update
  }

  $word = word::construct($this->entity->name());
  $word->recollect();
  $word->must_be_updated()->__destruct(); // update
  
  return $this;

 }
 public function remove_parent ($id) {

  $this->entity->parents()->remove($id); 
  $this->update_entity();

  if (entity::exists($id)) { 
   $entity = entity::construct($id);
   $entity->children()->remove($this->entity->id());
   $entity->must_be_updated()->__destruct(); // update
  }

  $word = word::construct($this->entity->name());
  $word->recollect();
  $word->must_be_updated()->__destruct(); // update
  
  return $this;
  
 }
 public function delete_child ($id) {
  $this->remove_child($id);
  entity::remove($id);
 }
 public function delete_parent ($id) {
  $this->remove_parent($id);
  entity::remove($id);
 }
 public function up_child ($id) {
  $this->entity->children()->up($id);
  return $this;
 }
 public function up_parent ($id) {
  $this->entity->parents()->up($id);
  return $this;
 }
 public function down_child ($id) {
  $this->entity->children()->down($id);
  return $this;
 }
 public function down_parent ($id) {
  $this->entity->parents()->down($id);
  return $this;
 }
 public function change_view ($view) { // TODO: remove risky function calls from $view
  $old_error = serialize(error_get_last());
  
  try {
   $old_error_handler = set_error_handler(array(__CLASS__, 'view_error_handler'));
   $display = $this->entity->display(); // try out
   restore_error_handler();
  } catch (Exception $e) {
   echo 'Caught exception: ',  $e->getMessage(), "\n";
   exit();
  }
  
  $new_error = serialize(error_get_last());
  if ($old_error == $new_error) {
   $this->entity->set_view($view);
   $this->update_entity();
  }
  return $this;
 }
 public function view_to_default ($view) {
  if ($view != entity::USE_DEFAULT_VIEW) {
   $word = word::construct($this->entity->name())
    ->must_be_updated()
    ->set_view($view)
   ;
   unset($word); // update
  }
 }
 public function view_from_default () {
  $word = word::construct($this->entity->name());
  $this->entity->set_view($word->view());
 }
 public function delete_view () {
  $this->entity->set_view('');
  return $this;
 }
 public function take_entity () {
  $this->entity->take_ownership($this->owner);
 }
 public function drop_entity () {
  $this->entity->drop_ownership($this->owner);
 }
 public function give_entity ($owner) {
  $this->entity->give_ownership($this->owner, $owner);
 }

 public function user_can_change () {
  return ($this->entity->owner() == '' || $this->entity->owner() == $this->owner);
 }
 
 public function add_to_query ($command, $word) {
  if (!empty($word) && word::exists($word)) {
   switch ($command) {
    case '':
     if (empty($this->search_query)) {
      $this->search_query = $word;
     }
     break;
    case 'find_below':
     if (!empty($this->search_query)) {
      $this->search_query .= '?[.'.$word.']';
     }
     break;
    case 'find_above':
     if (!empty($this->search_query)) {
      $this->search_query .= '?[:'.$word.']';
     }
     break;
    case 'with':
     if (!empty($this->search_query)) {
      $this->search_query .= '+[.'.$word.']';
     }
     break;
    case 'without':
     if (!empty($this->search_query)) {
      $this->search_query .= '-[.'.$word.']';
     }
     break;
    case 'in':
     if (!empty($this->search_query)) {
      $this->search_query .= '+[:'.$word.']';
     }
     break;
    case 'not_in':
     if (!empty($this->search_query)) {
      $this->search_query .= '-[:'.$word.']';
     }
     break;
    case 'that_is_with':
     if (!empty($this->search_query) && strrpos($this->search_query, ']') == strlen($this->search_query)-1) {
      $this->search_query = substr($this->search_query, 0, strlen($this->search_query)-1);
      $this->search_query .= '.'.$word.']';
     }
     break;
    case 'that_is_in':
     if (!empty($this->search_query) && strrpos($this->search_query, ']') == strlen($this->search_query)-1) {
      $this->search_query = substr($this->search_query, 0, strlen($this->search_query)-1);
      $this->search_query .= ':'.$word.']';
     }
     break;
   }
  }
  return $this;
 }

 
 public function html () {
  $user_can_change_class = '';
  if ($this->user_can_change()) {
   $user_can_change_class = ' user_can_change ';
  }
  
  $html = '
    <head>
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
     <title></title>
     <style>
      * { font-family: sans-serif; }
      body, table {padding:0; margin:0;}
      form,div,a { padding:0; margin: 0; }
      td { vertical-align: top; padding: 10px; }
      textarea { width: 100%; height: 200px; }
      ul.links li form { display: inline; }
      .options form { display: inline; }
      .layout.general { width: 100%; }
      .layout.general td.title { width: 150px; font-weight: bold; text-align: right; }
      form.button { display: inline; }
      a.node { text-decoration: none; }
      a.node:hover { text-decoration: underline; }
      .node.parent { color: green; }
      .node.child { color: navy; }
      .node.self { color: red; font-weight: bold; }
      .referrer { color: purple; }
      .user_can_change { background-color: #eeffee; }
     </style>
    </head>
    <body>
     <div class="panel '.$user_can_change_class.'">
      <table class="layout general">
       <tr>
        <td class="title">Id:</td>
        <td>
         '.$this->entity->id().'
        </td>
       </tr>
       <tr>
        <td class="title">Name:</td>
        <td>
         <form method="post">
          <input class="node self" type="text" name="name" value="'.$this->entity->name().'" />
          <input type="submit" value="Change" />
         </form>
        </td>
       </tr>
       <tr>
        <td class="title">Parents:</td>
        <td>
         '.$this->parent_list().'
         <form method="post">
          <input class="node parent" type="text" name="parent" value="" />
          <input type="submit" value="Add" />
         </form>
         '.$this->parent_options().'
        </td>
       </tr>
       <tr>
        <td class="title">Children:</td>
        <td>
         '.$this->child_list().'
         <form method="post">
          <input class="node child" type="text" name="child" value="" />
          <input type="submit" value="Add" />
         </form>
         '.$this->child_options().'
        </td>
       </tr>
       <tr>
        <td class="title">View:</td>
        <td>
         <form method="post">
          <textarea name="view">'.$this->entity->view().'</textarea>
          <input type="submit" name="view_change" value="Change" />
          <input type="submit" name="view_to_default" value="To default view" />
          <input type="submit" name="view_from_default" value="From default view" />
          <input type="submit" name="view_delete" value="Delete view" />
          <input type="submit" name="view_auto" value="Auto" />
         </form>
         <div>
         '.$this->entity->display().'
         </div>
        </td>
       </tr>
       <tr>
        <td class="title">Ownership:</td>
        <td>
         <div>
         '.$this->ownership_form().'
         </div>
        </td>
       </tr>
       <tr>
        <td class="title">Search:</td>
        <td>
         <form method="post">
          '.$this->search_command_list().'
          '.$this->dictionary_list().'
          <input type="submit" name="update_query" value="Update query" />
          <textarea name="search_query">'.$this->search_query.'</textarea>
          <div><pre>'.query::notation_to_code($this->search_query).'</pre></div>
          '.$this->search_list().'
         </form>
        </td>
       </tr>
       <tr>
        <td class="title">Panel:</td>
        <td>
         <form method="post" action="?entity_id='.$this->entity->id().'&panel=off">
          <input type="submit" value="Off" />
         </form>
        </td>
       </tr>
      </table>
     </div>
    </body>
  ';
  
  return $html;
 }

 public static function view_error_handler ($errno, $errstr, $errfile, $errline) {
  switch ($errno) {
   case E_USER_ERROR:
    echo "<b>View ERROR</b> [$errno] $errstr<br />\n";
    echo "  Fatal error on line $errline in file $errfile";
    echo ", PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />\n";
    echo "Aborting...<br />\n";
    exit(1);
    break;

   case E_USER_WARNING:
    echo "<b>View WARNING</b> [$errno] $errstr<br />\n";
    break;

   case E_USER_NOTICE:
    echo "<b>View NOTICE</b> [$errno] $errstr<br />\n";
    break;

   default:
    echo "Unknown error type in view: [$errno] $errstr<br />\n";
    break;
  }
  /* Don't execute PHP internal error handler */
  return true;
 }
 
 private function child_list () {
  
  $list = "\n";
  
  if ($this->entity->children()->count() > 0) { 
   $list .= '<table>';
   foreach($this->entity->children()->to_array() as $id) {
    if (entity::exists($id)) {
     $entity = entity::construct($id);
     $referrer_mark = '';
     if (isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], 'entity_id='.$entity->id()) !== false) {
      $referrer_mark = '<sup class="referrer">*</sup>';
     }
     $title = substr(strip_tags(strtr($entity->display(), "\n\t", '  ')), 0, 20);
     $list .= '<tr>'."\n";
     $list .= '<td><a class="child node" href="?entity_id='.$entity->id().'&panel=on" title="'.$title.'">'.$entity->name().'</a> '.$referrer_mark.'</td>'."\n";
     $list .= '<td>';
     $list .= '<form class="button" method="post"><input type="hidden" name="remove_child" value="'.$entity->id().'" /><input type="submit" value="X" /></form>'."\n";
     $list .= '<form class="button" method="post"><input type="hidden" name="delete_child" value="'.$entity->id().'" /><input type="submit" value="#" /></form>'."\n";
     $list .= '<form class="button" method="post"><input type="hidden" name="up_child" value="'.$entity->id().'" /><input type="submit" value="<" /></form>'."\n";
     $list .= '<form class="button" method="post"><input type="hidden" name="down_child" value="'.$entity->id().'" /><input type="submit" value=">" /></form>'."\n";
     $list .= '</td>';
     $list .= '</tr>'."\n";
    }else{
     // remove entity
    }
   }
   $list .= '</table>';
  }
  
  return $list;
 }
 private function parent_list () {
  //$word = word::construct($this->entity->name());
  $list = "\n";
  if ($this->entity->parents()->count() > 0) { 
   $list .= '<table>';
   foreach($this->entity->parents()->to_array() as $id) {
    if (entity::exists($id)) {
     $entity = entity::construct($id);
     $referrer_mark = '';
     if (isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], 'entity_id='.$entity->id()) !== false) {
      $referrer_mark = '<sup class="referrer">*</sup>';
     }
     $title = substr(strip_tags(strtr($entity->display(), "\n\t", '  ')), 0, 20);
     $list .= '<tr>'."\n";
     $list .= '<td><a class="parent node" href="?entity_id='.$entity->id().'&panel=on" title="'.$title.'">'.$entity->name().'</a> '.$referrer_mark.'</td>'."\n";
     $list .= '<td>';
     $list .= '<form class="button" method="post"><input type="hidden" name="remove_parent" value="'.$entity->id().'" /><input type="submit" value="X" /></form>'."\n";
     $list .= '<form class="button" method="post"><input type="hidden" name="delete_parent" value="'.$entity->id().'" /><input type="submit" value="#" /></form>'."\n";
     $list .= '<form class="button" method="post"><input type="hidden" name="up_parent" value="'.$entity->id().'" /><input type="submit" value="<" /></form>'."\n";
     $list .= '<form class="button" method="post"><input type="hidden" name="down_parent" value="'.$entity->id().'" /><input type="submit" value=">" /></form>'."\n";
     $list .= '</td>';
     $list .= '</tr>'."\n";
    }else{
     // remove entity
    }
   }
   $list .= '</table>';
  }
  return $list;
 }

 private function child_options () {
  
  $list = "\n";

  if ($this->entity->name() != '' && word::exists($this->entity->name())) {
   $word = word::construct($this->entity->name());
   if ($word->children()->count() > 0) { 
    $list .= '<div class="options">';
    foreach($word->children()->to_array() as $id) {
     $list .= '<form method="post"><input type="hidden" name="child" value="'.$id.'" /><input type="submit" value="'.$id.'" /></form>'."\n";
    }
    $list .= '</div>';
   }
  }
  
  return $list;
 }
 private function parent_options () {
  
  $list = "\n";

  if ($this->entity->name() != '' && word::exists($this->entity->name())) {
   $word = word::construct($this->entity->name());
   if ($word->parents()->count() > 0) { 
    $list .= '<div class="options">';
    foreach($word->parents()->to_array() as $id) {
    $list .= '<form method="post"><input type="hidden" name="parent" value="'.$id.'" /><input type="submit" value="'.$id.'" /></form>'."\n";
    }
    $list .= '</div>';
   }
  }
  
  return $list;
 }

 private function ownership_form () {
  $html = '';
  
  if ($this->entity->owner() == '') {
   $html .= '<div>Entity belongs to the public.</div>';
   if ($this->owner != '') {
    $html .= '<form method="post">';
    $html .= '<input type="hidden" name="take_entity" value="" />';
    $html .= '<input type="submit" value="Take ownership" />';
    $html .= '</form>';
   }
  }else{
   if ($this->entity->owner() != $this->owner) {
    $html .= '<div>Entity belongs to '.$this->entity->owner().'</div>';
   }else{
    $html .= '<div>Entity belongs to you.</div>';
    $html .= '<form method="post">';
    $html .= '<input type="hidden" name="drop_entity" value="" />';
    $html .= '<input type="submit" value="Drop ownership" />';
    $html .= '</form>';
    $html .= '<form method="post">';
    $html .= '<span>New owner: </span>';    
    $html .= '<input type="text" name="new_owner" value="" />';
    $html .= '<input type="hidden" name="give_entity" value="" />';
    $html .= '<input type="submit" value="Give ownership" />';
    $html .= '</form>';
   }
  }
  
  if ($this->entity->owner() != $this->owner) {
  
  }
  
  if (empty($this->owner)) {
   $html .= '<form method="post">';
   $html .= '<span>E-mail: </span>';
   $html .= '<input type="text" name="email" value="" />';
   $html .= '<span>Password: </span>';
   $html .= '<input type="password" name="password" value="" />';
   $html .= '<input type="submit" value="Log in" />';
   $html .= '</form>';
  }else{
   $html .= '<div>You are '.$this->owner.'</div>';   
   $html .= '<form method="post">';
   $html .= '<input type="hidden" name="logout" value="" />';
   $html .= '<input type="submit" value="Log out" />';
   $html .= '</form>';
  }
  
  if ($this->entity->owner() == '' || $this->entity->owner() == $this->owner) {
   $html .= '<div>You can edit this entity.</div>'; 
  }else{
   $html .= '<div>You can not edit this entity.</div>'; 
  }

  
  return $html;
 }

 private function dictionary_list () {
  $dictionary_list = '<select name="dictionary">';
  $dictionary_list .= '<option value=""></option>';
  foreach (word::dictionary() as $word) {
   $dictionary_list .= '<option value="'.$word.'">'.$word.'</option>';
  }
  $dictionary_list .= '</select>';
  return $dictionary_list;
 }
 private function search_command_list () {
  return '
   <select name="search_command">
    <option value=""></option>
    <option value="find_above">find above</option>
    <option value="find_below">find below</option>
    <option value="with">with</option>
    <option value="without">without</option>
    <option value="in">in</option>
    <option value="not_in">not in</option>
    <option value="that_is_with">that is with</option>
    <option value="that_is_in">that is in</option>
   </select>
  ';
 }
 private function search_list () {
  $search_list = '';
  if (!empty($this->search_query)) {
   $q = query::construct();
   $q = eval('return '.query::notation_to_code($this->search_query).';');
   $search_list = $q->to_menu();
  }
  return $search_list;
 }
 
 
 private function update_entity () {
  $my_id = $this->entity->id();
  $this->entity->must_be_updated()->__destruct();
  $this->entity = entity::construct($my_id);
 }

 
}


query::set_script_start_time();

// session
session_start();

// panel on/off
if (isset($_GET['panel'])) {
 if (in_array($_GET['panel'], array('1','on','yes'))) {
  $_SESSION['panel_on'] = true;
 }elseif(in_array($_GET['panel'], array('0','off','no'))) {
  $_SESSION['panel_on'] = false;
 }
}
$panel_on = isset($_SESSION['panel_on']) ? $_SESSION['panel_on'] : false;

// owner set/unset
if ($panel_on) {

 if (isset($_POST['email'], $_POST['password'])) {
  $email = $_POST['email'];
  $password = $_POST['password'];
  $encripted_password = function_exists('encript_password') ? encript_password($password) : md5($password);
  $_SESSION['owner'] = entity::compose_owner($email, $encripted_password);
 }
 if (isset($_POST['logout'])) {
  unset($_SESSION['owner']);
 }
 
 
}
$owner = isset($_SESSION['owner']) ? $_SESSION['owner'] : '';


$default_entity_id = '_00000000000000000000000000000000';
if (!entity::exists($default_entity_id)) {
 entity::construct($default_entity_id)->set_name('default entity')->must_be_updated();
}
$entity_id = (isset($_GET['entity_id'])) ? $_GET['entity_id'] : $default_entity_id;


if (entity::exists($entity_id)) {
 $entity = entity::construct($entity_id);
 if ($panel_on) {
  
  $entity->must_be_updated();
 
  $panel = panel::construct()
   ->set_entity($entity)
   ->set_owner($owner)
  ;
  
  if ($panel->user_can_change()) {
   
   if (isset($_POST['name'])) {
    $panel->change_name($_POST['name']);
   }
   if (isset($_POST['parent'])) {
    $panel->add_parent($_POST['parent']);
   }
   if (isset($_POST['child'])) {
    $panel->add_child($_POST['child']);
   }
   if (isset($_POST['remove_parent'])) {
    $panel->remove_parent($_POST['remove_parent']);
   }
   if (isset($_POST['remove_child'])) {
    $panel->remove_child($_POST['remove_child']);
   }
   if (isset($_POST['delete_parent'])) {
    $panel->delete_parent($_POST['delete_parent']);
   }
   if (isset($_POST['delete_child'])) {
    $panel->delete_child($_POST['delete_child']);
   }
   if (isset($_POST['up_parent'])) {
    $panel->up_parent($_POST['up_parent']);
   }
   if (isset($_POST['up_child'])) {
    $panel->up_child($_POST['up_child']);
   }
   if (isset($_POST['down_parent'])) {
    $panel->down_parent($_POST['down_parent']);
   }
   if (isset($_POST['down_child'])) {
    $panel->down_child($_POST['down_child']);
   }
   if (isset($_POST['view'])) {
    if (isset($_POST['view_change'])) {
     $panel->change_view($_POST['view']);
    }   
    if (isset($_POST['view_to_default'])) {
     $panel->view_to_default($_POST['view']);
    }   
    if (isset($_POST['view_from_default'])) {
     $panel->view_from_default();
    }   
    if (isset($_POST['view_delete'])) {
     $panel->delete_view();
    }   
    if (isset($_POST['view_auto'])) {
     $panel->change_view(entity::USE_DEFAULT_VIEW);
    }   
   }

   if (isset($_POST['take_entity'])) {
    $panel->take_entity();
   }
   if (isset($_POST['drop_entity'])) {
    $panel->drop_entity();
   }
   if (isset($_POST['give_entity'], $_POST['new_owner']) && !empty($_POST['new_owner'])) {
    $panel->give_entity($_POST['new_owner']);
   }
   
   if (isset($_POST['search_query'])) {
    $panel->set_search_query($_POST['search_query']);
    if (isset($_POST['update_query'], $_POST['dictionary'], $_POST['search_command'])) {
     $panel->add_to_query($_POST['search_command'], $_POST['dictionary']);
    }
   }   
   
  }

 
  echo $panel->html();
  
 }else{
  echo $entity->display();
 }
}



?>

понедельник, 3 ноября 2014 г.

URI в художественном тексте

Обнаружил нечто похожее на адреса в художественном тексте. Приведу исследуемый отрывок рассказа Марка Твена:

It was summer time, and twilight. We were sitting on the porch of the farm-house, on the summit of the hill, and "Aunt Rachel" was sitting respectfully below our level, on the steps, -- for she was our servant, and colored. She was of mighty frame and stature; she was sixty years old, but her eye was undimmed and her strength unabated.
Ниже приведу URI, ведущие от большего к меньшему.

time
time/summer
time/summer/twilight

hill
hill/summit
hill/summit/farm-house
hill/summit/farm-house/porch
hill/summit/farm-house/porch/we
hill/summit/farm-house/steps
hill/summit/farm-house/steps/aunt

пятница, 31 октября 2014 г.

Что влияет на что

Иногда положение слова в предложении определяет его смысловое содержание.

Pete liked Jess. Jess didn't like Pete.
Jess liked Pete. Pete didn't like Jess.

Проблем нет, когда перед нами экскаватор и яма. Понятно, что экскаватор копает яму, а не наоборот.

Когда источником воздействия могут по смыслу быть оба предмета и они отвечают на один вопрос, текст становится запутанным.

like who: jess who: pete
not like who: jess who: pete

Решением может прийти из театра. Вот на сцене два персонажа. Один доволен таким соседством, а другой нет.

live who: pete where: here
live who: jess where: here
be happy who: pete
be unhappy who: jess


P.S.

Пример приведён не очень удачный, поскольку в английском для одушествлённых предметов есть вопросительное слово - whom.

Без окончаний

Когда смысловае ввязи названы явно отпадает надобность в использовании окончаний.

приходить кто: босяки куда: в деревня когда: в праздники

О минусах скажут другие, перечислю плюсы.

1. Любой естесственный язык может быть преобразован к такому виду без утраты смысла. Люди переходят на такой режим передачи сообщений при плохом качестве канала связи.

- В праздники босяки придут в деревню.
- Кто придёт?
- Босяки.
- Тусяки?
- Нет, не тусяки. Босяки.
- Косяки? Ты чё?
- Босяк. Босяки. Босые люди. Без обуви.

Общение туристов с местным населением на незнакомом языке тоже может проходить в подобном формате. Турист может произнести глагол, а на прочие составляющие просто указать пальцем.

Т.е. данный алгоритм применим к любому языку. Сообщение звучит станновато, но его смысл носители языка понимают полностью.

put who: gentleman what: paper where: on lap

2. Пропадает необходимость в программе анализировать формы слов. В существующих языках масса правил и ислючений. Врятли кто-либо когда-либо сможет формализировать все правила для всех языков в виде программного кода.

четверг, 30 октября 2014 г.

А.П. Чехов. Новая дача


Попытаюсь преобразовать начало рассказа А.П. Чехова "Новая дача" в объектно-ориентированный формат.

   В трех верстах от деревни Обручановой строился громадный мост. Из деревни, стоявшей высоко на крутом берегу, был виден его решётчатый остов, и в туманную погоду и в тихие зимние дни, когда его тонкие железные стропила и все леса кругом были покрыты инеем, он представлял живописную и даже фантастическую картину. Через деревню проезжал иногда на беговых дрожках или в коляске инженер Кучеров, строитель моста, полный, плечистый, бородатый мужчина в мягкой, помятой фуражке; иногда в праздники приходили босяки, работавшие на мосту; они просили милостыню, смеялись над бабами и, случалось, уносили что-нибудь. Но это бывало редко; обыкновенно же дни проходили тихо и спокойно, как будто постройки не было вовсе, и только по вечерам, когда около моста светились костры, ветер слабо доносил песню босяков. И днем иногда слышался печальный металлический звук: дон... дон... дон...

Начали!
 
На сцене появляется деревня.
Деревня называется Обручановая.
На сцене появляется мост.
Мост строился в трёхстах километрах от деревни.
Мост строился громадный.
На сцене появляется берег.
Берег был крутой.
Деревня стояла на берегу.
Деревня была высоко.
На сцене появляется остов.
Остов был у моста.
Остов был решётчатый.
Остов был виден из деревни.
На сцене появляется погода.
Погода была туманная.
На сцене появляются дни.
Дни были зимние и тихие.
На сцене появляются стропила.
Стропила были у моста.
Стропила были тонкие.
Стропила были железные.
На сцене появляются леса.
Леса были кругом.
На сцене появляется иней.
Стропила и леса были покрыты инеем.
На сцене появляется картина.
Картина была живописная и даже фантастическая.
Мост представлял собой картину.
На сцене появляются дрожки.
Дрожки были беговые.
На сцене появляется коляска.
На сцене появляется инженер.
Инженера звали Кучеров.
Инженер проезжал иногда через деревню на дрожках или в коляске.
Инженер был строителем моста.
Инженер был полный плечистый бородатый мужчина.
На сцене появляется фуражка.
Фуражка была помятая.
Инженер был в фуражке.
Со сцены уходит погода.
Со сцены уходят дни.
Со сцены уходят дрожки.
Со сцены уходит коляска.
Со сцены уходит инженер.
Со сцены уходит фуражка.
На сцене появляются праздники.
На сцене появляются босяки.
В празники босяки приходили в деревню.
Босяки работали на мосту.
На сцене появляется милостыня.
Босяки просили милостыню.
На сцене появляются бабы.
Босяки смеялись над бабами.
Босяки иногда уносили что-нибудь.
Босяки смеялись редко.
Босяки уносили редко.
Со сцены уходят праздники.
Со сцены уходят босяки.
Со сцены уходит милостыня.
Со сцены уходят бабы.
На сцене появляются дни.
Дни проходили обыкновенно тихо и спокойно.
На сцене появляется постройка.
Выполнялась постройка моста.
Постройки как будто не было вовсе.
Со сцены уходят дни.
На сцене появляются вечера.
На сцене появляются костры.
Костры светились около моста по вечерам.
На сцене появляется ветер.
На сцене появляется песня.
На сцену возвращаются босяки.
Ветер слабо доносил песню босяков.
Со сцены уходят вечера.
Со сцены уходят костры.
Со сцены уходит ветер.
Со сцены уходит песня.
На сцене появляется день.
На сцене появляется звук.
Днём иногда слышался печальный металлический звук.
Звук был дон... дон... дон...

Занавес!

В таком представлении информации у одного предмета одно название. Здесь нет местоимений. Появление и уход объектов со сцены объявлены, что позволяет простым способом разложить предложения на части. Важно явно выделить глагол, поскольку он несёт сведения об изменении. Здесь глагол подчёркнут, но можно было бы его ставить везде на первое место. Немцы, например, ставят значимую часть сказуемого в конец предложения. Это им не мешает общаться.

Сложные предложения отсутствуют. В песне поётся так. "Жираф большой. Ему виднее." Мы нормально понимаем это как то, что жирафу виднее, потому что он большой. Это феномен заслуживает отдельного изучения.

Есть некоторые сложности со словом "это", которое относится к целому отрывку. В нашем случае, затруднение вызвало слово "постройка".

Вместо фразы "на сцене появляется" можно было бы ставить плюс, а вместо фразы "со сцены уходит" знак минус, сократив тем самым трудозатраты на написание текста. Вместо фразы "на сцену возвращаются" пока не ясно, какой знак подошёл бы лучше всего.

Цель компьютерной обработки - преобразовать этот текст в сеть связанных узлов (граф).

Интересно отметить, что в ходе переработки исходного материала повествование было разбито на отдельные части (эпизоды) в тех местах, где сразу много предметов уходит со сцены.

Особый случай составляют объекты, которые уходят со сцены, а потом через некоторое время возвращаются на неё. Такие объекты участвуют в нескольких эпизодах. Они заслуживают особого анализа. Их можно использовать в качестве смысловых меток при описании смысла теста.

Интересны также объекты, которые долго остаются на сцене, и те объекты, которые имеют много связей.

Иными словами, можно кратко охарактеризовать данный фрагмент как текст об инженере и босяках в деревне.

+деревня
Называется деревня Обручановая.
+мост
Строился мост в трёхстах километрах от деревни.
Строился мост громадный.
+берег
Был берег крутой.
Стояла деревня на берегу.
Была деревня высоко.
+остов
Был остов у моста.
Был остов решётчатый.
Был остов виден из деревни.
+погода
Была погода туманная.
+дни
Были дни зимние и тихие.
+стропила
Были стропила у моста.
Были стропила тонкие.
Были стропила железные.
+леса
Были леса кругом.
+иней
Были покрыты инеем стропила и леса.
+картина
Была картина живописная и даже фантастическая.
Представлял собой мост картину.
+дрожки
Были дрожки беговые.
+коляска,инженер
Звали инженера Кучеров.
Проезжал инженер иногда через деревню на дрожках или в коляске.
Был инженер строителем моста.
Был инженер полный плечистый бородатый мужчина.
+фуражка
Была фуражка помятая.
Был инженер в фуражке.
-погода,дни,дрожки,коляска,инженер,фуражка
+праздники,босяки
Приходили босяки в деревню в праздники.
Работали босяки на мосту.
+милостыня
Просили босяки милостыню.
+бабы
Смеялись босяки над бабами.
Уносили босяки иногда что-нибудь.
Смеялись босяки редко.
Уносили босяки редко.
-праздники,босяки,милостыня,бабы
+дни
Проходили дни обыкновенно тихо и спокойно.
+постройка
Выполнялась постройка моста.
Не было постройки как будто вовсе.
-дни
+вечера,костры
Светились костры около моста по вечерам.
+ветер,песня
=босяки
Доносил ветер слабо песню босяков.
-вечера,костры,ветер,песня
+день,звук
Слышался днём иногда печальный металлический звук.
Был звук дон... дон... дон...

Эта запись напоминает такой стиль устного сообщения:

Мост. Какой был мост!
А деревня. Вы помните, какая была деревня?
Ну а инженер? Инженер был что надо.
Вот только босяки. Босяки воровали.
Какие были дни!

Есть мысли по поводу редактора для такого рода записи. Скажем у вас есть сенсорный экран. Вы напечатали небольшой фрагмент повествования. Затем жестами выкидываете слова из текста. Задача редактора - найти неопределённую форму выброшенного слова и ввести предмет на сцену его в нужном месте выше по тексту.

Можно вести и звуковую запись. Новое слово - новое прикосновение к экрану. На этом месте появляется значок. Если коснуться его ещё раз, слово будет воспроизведено через динамик. Значки можно выкидывать жестами с экрана. Мне кажется, что такая запись будет быстрее печати на клавиатуре. Структура создаваемого графа может быть использована, как вспомогательное средство при переводе голоса в текст.

Communicate with computers effectively

To interact with the computer world effectivly the humanity has to learn speaking a different langage. I think it is improbable that someone creates a computer program or a semantic model that allows the computer to deal with human knowledge in an effective way. We must collectively create a base of well structured texts. Only having this resource at its disposal the computer can analyse existing poorly structured texts. There is no way around. But business still hopes or tries to be happy with the technologies of yesterday. I think the language must be like a script for a play describing actors and things that come to the stage, perform some act and afterwards leave the stage.

суббота, 25 октября 2014 г.

Семантический JSON


Текст с обратной ссылкой

[
    {
        сижу: {
            кто: я,
            где: {
                на стуле: {
                    где: перед компьютером
                }
            }
        }
    },
    {
        работает: {
            что: {
                компьютер: {
                    сижу: {
                        где: {
                            на стуле: {
                                где: ?
                        }
                    }
                }
            }
        }
    }
]



Вопрос


[{
    сижу: {
        кто: я,
        где: ?
    }
}]


Вопрос о действии

[{
    ?: {
        что: компьютер
    }
}]



Правила

Перечисления заключаются в прямоугольные скобки

Блоки заключаются в фигурные скобки

Сообщение состоит из перечисления действий

Действие заключено в фигурные скобки (является блоком)

У действия только один ключ - сказуемое

К ключу действия относится блок уточняющей информации

Блок уточняющей информации может быть пуст

Ключами в блоке уточняющей информации являются вопросительные слова - кто, что, когда, как, почему, зачем и др.

Ключу в блоке уточняющей информации может быть сопоставлено:
- слово
- блок
- перечисление слов и блоков

Если ключу в блоке уточняющей информации сопоставлен блок или перечисление блоков, каждый из которых имеет единственный ключ-действие, то речь идёт о подчинённых предложениях

Если ключу в блоке уточняющей информации сопоставлен блок, ключ которого не является действием, то речь идёт о сложном уточнении

В сложном уточнении есть только один ключ, который отвечает на соответствующий вопрос

В сложном уточнении за ключом следует либо вложенный блок уточнений, либо обратная ссылка

Отличительные черты обратной ссылки:
- ключом является сказуемое
- сопоставлена ключу, который не является вопросительным словом
- содержит вопросительный знак, который стоит на месте другого названия этого же предмета выше по тексту
- является полной или урезанной копией одного из действий выше по тексту

Другие мысли здесь и прототип здесь.