I'm writing a function to count syllables of a word as follows:
function estimate_syllables($word) {
$total_count=0;
foreach($word as $w) {
$syllable_count = count_english_vowels($w);
$total_count += $syllable_count;
}
return $total_count;
}
function count_english_vowels($word) {
static $english_vowels = array('A', 'E', 'I', 'O', 'U', 'Y');
$vowel_count = 0;
$letters = preg_replace('/[^a-z0-9]+/i', "", $word);
$len = strlen($letters);
$letters = str_split(strtoupper($letters));
$currPosition = -2;
$prevPosition = -1;
for($i = 0; $i < $len; $i++) {
if (in_array($letters[$i], $english_vowels)) {
if($i != $currPosition + 1) {
if ($letters[$i] == 'E' && $i != ($len -1))
$vowel_count++;
$prevPosition = $currPosition;
$currPosition = $i;
}
}
}
return $vowel_count;
}
I'm really confused: if you pass count_english_vowels a word like water, it reaches the inner loop to the correct syllable twice, yet the counter only reports a 1? Super confused, can anyone figure out what's going wrong?
It's only reporting one because of this:
if ($letters[$i] == 'E' && $i != ($len -1))
$vowel_count++;
$prevPosition = $currPosition;
$currPosition = $i;
There is only one E in water, thus it's only executing $vowel_count++; once. Based on your indenting of this code, I'd assume you need {} brackets around those lines, as without them PHP will only apply the first line after an if without them, so if that code is correct your indentation should look like this:
if ($letters[$i] == 'E' && $i != ($len -1))
$vowel_count++;
$prevPosition = $currPosition;
$currPosition = $i;
Related
I have homework to do this program in PHP, that take a matrix and the keywords, so it can find them in the matrix diagonally:
So this is how the first matrix looks, there are many with different keywords, here for instance keywords are "beef" and "pork". So I made a program for an input that looks like these 2 examples:
There correct input for these two is here:
Here is my code that works only in the first case, I don't know how to make a loop that will make more and more conditions instead of writing down all these conditions with && in if statement.
Please give me some tips on how to do it:
<?PHP
$input_line = trim(fgets(STDIN));
$input_array = explode(" ",$input_line);
// get mojiban
$mojiban = array();
for($ix=0; $ix<$input_array[0];$ix++){
$mojiban[] = str_split(trim(fgets(STDIN)));
}
//get words
$words = array();
for( $kx = 0; $kx < $input_array[1]; $kx++ ){
$words[] = trim(fgets(STDIN));
}
////check verticales
//get word
$wordCharArray = array();
for($dx=0; $dx < $input_array[1]; $dx++){
$wordCharArray = str_split($words[$dx]);
//looping and checking
for ( $line = 0; $line < $input_array[0] ; $line++) {
for ( $column = 0; $column < $input_array[0] ; $column++ ){
// for ($wordsNumber = 0; $wordsNumber < $input_array[1] ; $wordsNumber++){
if ($mojiban[$line][$column] == $wordCharArray[0] && $mojiban[$line+1][$column+1] == $wordCharArray[1]) {
echo ($column+1)." ".($line+1)."\n";
//}
}
}
}
Thanks in Advance!
Here's a solution to your problem right now it only checks for diagonal elements. You can refactor as you want from below.
There are many different solutions but the solution for your code I have pasted first checks for the character match in the array. If there is a match, it proceeds to check diagonally for the element from the position that it found the first element.
<?php
// First martix
// $searchWords = ["BEEF", "PORK"];
// $matrix = ["HPPLLM", "UROQUV", "FBSRZY", "DPEFKT", "GBBEUY", "EMCQFY"];
// Second martix
$searchWords = ["ABA", "BAB"];
$matrix = ["ACEG", "HBDF", "EGAC", "DFHB"];
// returns true if it diagonally matches the element in matrix from start row
function checkDiagonallyForString(string $search, array $matrix, int $startRow, int $firstMatchPosition)
{
$endRow = $startRow + (strlen($search) - 2);
$finding = true;
foreach (range($startRow, $endRow) as $searchIndex => $rowValue) {
if (!$finding) {
break;
}
$char = $search[$searchIndex + 1];
$finding = $matrix[$rowValue][$firstMatchPosition + $searchIndex] == $char;
}
return $finding;
}
// format: [word: [column, row]]
$found = [];
foreach ($matrix as $row => $matrixString) {
if (!count($searchWords)) {
break;
}
foreach ($searchWords as $wordRow => $word) {
$position = strpos($matrixString, $word[1]);
if ($position != false) {
if (checkDiagonallyForString($word, $matrix, $row, $position)) {
// $position = column of matrix
// $row = row of matrix
$found[$word] = [$position, $row];
unset($searchWords[$wordRow]);
}
}
}
}
// Pretty output
echo "<pre>";
print_r($found);
foreach ($found as $word => $indexes) {
echo $word . " " . implode(" ", $indexes) . "\n";
}
The results are:
Note: This is a complete answer but before copying and pasting this please try to get a general idea and attempt to solve it yourself. This is not the only way of doing it.
We can use implode tactics from comments. Here is example: click.
Code:
// First martix
$searchWords = ["BEEF", "PORK"];
$matrix = ["HPPLLM", "UROQUV", "FBSRZY", "DPEFKT", "GBBEUY", "EMCQFY"];
// Second martix
// $searchWords = ["ABA", "BAB"];
// $matrix = ["ACEG", "HBDF", "EGAC", "DFHB"];
$matrixStr = implode('', $matrix);
$matrixSize = count($matrix);
$positions = [];
foreach ($searchWords as $wordIdx => $word) {
$wordSize = strlen($word);
$found = false;
for ($y = 0; $y <= $matrixSize - $wordSize && !$found; $y++) {
for ($x = 0; $x <= $matrixSize - $wordSize && !$found; $x++) {
$allLettersOk = true;
for ($l = 0; $l < $wordSize; $l++) {
if ($matrixStr[$y * $matrixSize + $x + $l * $matrixSize + $l] != $word[$l]) {
$allLettersOk = false;
break;
}
}
if ($allLettersOk) {
$positions[$word] = [$x + 1, $y + 1];
$found = true;
}
}
}
}
foreach ($positions as $word => $indexes) {
echo $word . " " . implode(" ", $indexes) . "\n";
}
If you need a description to this code - write a comment.
// First martix
$searchWords = ["BEEF", "PORK"];
$matrix = ["HPPLLM", "UROQUV", "FBSRZY", "DPEFKT", "GBBEUY", "EMCQFY"];
// Second martix
// $searchWords = ["ABA", "BAB"];
// $matrix = ["ACEG", "HBDF", "EGAC", "DFHB"];
$matrixStr = implode('', $matrix);
$matrixSize = count($matrix);
$positions = [];
foreach ($searchWords as $wordIdx => $word) {
$wordSize = strlen($word);
$found = false;
for ($y = 0; $y <= $matrixSize - $wordSize && !$found; $y++) {
for ($x = 0; $x <= $matrixSize - $wordSize && !$found; $x++) {
$allLettersOk = true;
for ($l = 0; $l < $wordSize; $l++) {
if ($matrixStr[$y * $matrixSize + $x + $l * $matrixSize + $l] != $word[$l]) {
$allLettersOk = false;
break;
}
}
if ($allLettersOk) {
$positions[$word] = [$x + 1, $y + 1];
$found = true;
}
}
}
}
foreach ($positions as $word => $indexes) {
echo $word . " " . implode(" ", $indexes) . "\n";
}
Your $input_array contains your matrix, at each position you have a character, like this
$input_array = [
['H', 'P', 'P', 'L', 'L', 'M'],
['U', 'R', 'O', 'Q', 'U', 'V'],
['F', 'B', 'S', 'R', 'Z', 'Y'],
['D', 'P', 'E', 'F', 'K', 'T'],
['G', 'B', 'B', 'E', 'U', 'Y'],
['E', 'M', 'C', 'Q', 'F', 'Y'],
];
For the sake of simplicity, let's assume that you also have an array of arrays. I know you have strings, but string operations would make this solution difficult to read and we are mostly interested in the algorithm, so, for the sake of understandability I will not start from your actual data structure, but will answer questions if you have difficulty implementing this into your solution:
$words = [
['B', 'E', 'E', 'F'],
['P', 'O', 'R', 'K']
];
Notice that beef and pork are of the same length, but let's not assume that all words are of the same length.
//Computing row count so it can be reused
$rowCount = count($input_array);
//Computing the column count so it can be reused
//We assume that each row of the matrix has the same number of columns
$colCount = count($input_array[0]);
//Looping the rows
for ($row = 0; $row < $rowCount; $row++) {
//Looping the columns of the row
for ($column = 0; $column < $colCount; $column++) {
//Computing the max length of allowed words
$maxLength = min($rowCount - $row, $colCount - $col);
//Looping the words
for ($wIndex = 0; $wIndex < count($words); $wIndex++) {
//Storing the length of the current word for later use
$charCount = count($words[$wIndex]);
//We avoid checking for the presence of words that are
//Longer than the current position allows
if ($charCount <= $maxLength) {
//match is initialilzed with true and will be set false at
//the first mismatch. If no mistmatch is found, then we know
//that the word is present at the current position
$match = true;
//Looping the word's characters and check for possible
//mismatches
//Notice that we stop the loop either at the first mismatch
//or at the last character if there is no mismatch
for ($cIndex = 0; $match && ($cIndex < $charCount); $cIndex++) {
//If the word character at cIndex offset mismatches
//the character of input array at the same offset, starting
//from the [$row][$column] position, then it mismatches
if ($words[$wIndex][$cIndex] !== $input_array[$row + $cIndex][$column + $cIndex]) $match = false;
}
if ($match) {
//Say the word
echo implode("", $words[$wIndex]) . " found at row " . ($row + 1) . ", column " . ($column + 1);
}
}
}
}
}
I have this function ...
public function getGroupName($no_of_participant) {
$groupNameArry = array();
$groupNumber = 0;
for($i = 0; $i <= $no_of_participant/2; $i++) { // loop for rows
for($letter = 'A'; $letter <= 'Z'; $letter++) { // loop for columns
if($groupNumber <= $no_of_participant/2) {
if($i == 0) {
$groupNameArry[$groupNumber] = $letter;
} else {
$groupNameArry[$groupNumber] = $letter.$i;
}
$groupNumber++;
}
}
}
return $groupNameArry;
}
The expected Result is
A-Z and then A1,B1,C1,D1 ....
But unexpectedly getting
A-Z and then AA, AB, AC ...
I am calling this function like as
$groupNameArry = $this->getGroupName(MAX_ALLOWED_PARTICIPANT);
where maximum allowed participant value is 100. what is wrong? Please help!
$letter is a string.
When $letter becomes 'Z', the condition $letter <= 'Z' is TRUE and it runs what you expect to be the last iteration.
After this iteration $letter++ increments 'Z' and it becomes 'AA'. This is how PHP handles ++ on strings.
The increment operator turning 'Z' into 'AA' is even given as example in the documentation of the increment operators (See Example #1).
Then the condition $letter <= 'Z' is still TRUE and it runs more iterations with $letter having the values 'AA', 'AB', 'AC' a.s.o.
The PHP way to iterate from 'A' to 'Z' is to use foreach on a range():
foreach (range('A', 'Z') as $letter) {
echo($letter);
}
echo("\nDone.");
The output is:
ABCDEFGHIJKLMNOPQRSTUVWXYZ
Done.
In you code $i = 0 in every loop !!
This fixes your issue.
/**
* Get group name
*
* #param int $no_of_participant
* #return string
*/
public static function getGroupName(int $no_of_participant)
{
$groupNameArry = array();
$groupNumber = 0;
$letters = range('A', 'Z');
foreach ($letters as $letter) {
for ($i = 0; $i <= $no_of_participant / 2; $i++) {
if ($i == 0) {
$groupNameArry[$groupNumber] = $letter;
} else {
$groupNameArry[$groupNumber] = $letter . $i;
}
$groupNumber++;
}
}
return $groupNameArry;
}
How can I write a function that gives me number of the character that is passed to it
For example, if the funciton name is GetCharacterNumber and I pass B to it then it should give me 2
GetCharacterNumber("A") // should print 1
GetCharacterNumber("C") // should print 3
GetCharacterNumber("Z") // should print 26
GetCharacterNumber("AA") // should print 27
GetCharacterNumber("AA") // should print 27
GetCharacterNumber("AC") // should print 29
Is it even possible to achieve this ?
There is a function called ord which gives you the ASCII number of the character.
ord($chr) - ord('A') + 1
gives you the correct result for one character. For longer strings, you can use a loop.
<?php
function GetCharacterNumber($str) {
$num = 0;
for ($i = 0; $i < strlen($str); $i++) {
$num = 26 * $num + ord($str[$i]) - 64;
}
return $num;
}
GetCharacterNumber("A"); //1
GetCharacterNumber("C"); //3
GetCharacterNumber("Z"); //26
GetCharacterNumber("AA"); //27
GetCharacterNumber("AC"); //29
GetCharacterNumber("BA"); //53
?>
Not very efficient but gets the job done:
function get_character_number($end)
{
$count = 1;
$char = 'A';
$end = strtoupper($end);
while ($char !== $end) {
$count++;
$char++;
}
return $count;
}
echo get_character_number('AA'); // 27
demo
This works because when you got something like $char = 'A' and do $char++, it will change to 'B', then 'C', 'D', … 'Z', 'AA', 'AB' and so on.
Note that this will become the slower the longer $end is. I would not recommend this for anything beyond 'ZZZZ' (475254 iterations) or if you need many lookups like that.
An better performing alternative would be
function get_character_number($string) {
$number = 0;
$string = strtoupper($string);
$dictionary = array_combine(range('A', 'Z'), range(1, 26));
for ($pos = 0; isset($string[$pos]); $pos++) {
$number += $dictionary[$string[$pos]] + $pos * 26 - $pos;
}
return $number;
}
echo get_character_number(''), PHP_EOL; // 0
echo get_character_number('Z'), PHP_EOL; // 26
echo get_character_number('AA'), PHP_EOL; // 27
demo
Use range and strpos:
$letter = 'z';
$alphabet = range('a', 'z');
$position = strpos($alphabet, $letter);
For double letters (eg zz) you'd probably need to create your own alphabet using a custom function:
$alphabet = range('a', 'z');
$dictionary = range('a','z');
foreach($alphabet AS $a1){
foreach($alphabet AS $a2) {
$dictionary[] = $a1 . $a2;
}
}
Then use $dictionary in place of $alphabet.
Here is the full code that does what you want.
Tested it and works perfectly for the examples you gave.
define('BASE', 26);
function singleOrd($chr) {
if (strlen($chr) == 1) {
return (ord($chr)-64);
} else{
return 0;
}
}
function multiOrd($string) {
if (strlen($string) == 0) {
return 0;
} elseif (strlen($string) == 1) {
return singleOrd($string);
} else{
$sum = 0;
for($i = strlen($string) - 1; $i >= 0; $i--) {
$sum += singleOrd($string[$i]) * pow(BASE, $i);
}
}
return $sum;
}
I think ord should be a more efficient way to have your number :
$string = strtolower($string);
$result = 0;
$length = strlen($string);
foreach($string as $key=>$value){
$result = ($length -$key - 1)*(ord($value)-ord(a)+1);
}
and result would contain what you want.
for ($i = 1; $i <= $numITER; $i++) {
$val = rand(2,36);
switch (TRUE) {
case ($val<=10):
$vercodeTEMP.=$val;
break;
case ($val>10):
if ($val != 25 && $val != 19) {
$vercodeTEMP.=chr(54+$val);
} else {
$i=$i-1;
}
break;
}
}
I'm basically trying to avoid 0, 1, and the letters O and I. How can this possibly be giving me zero's when the rand range is 2 to 36?
If $val == 10, then you will append $val onto $vercodeTEMP.
Try:
for ($i = 1; $i <= $numITER; $i++) {
$val = rand(2,36);
if ($val<10) {
$vercodeTEMP.=$val;
} else if ($val != 25 && $val != 19) {
$vercodeTEMP.=chr(54+$val);
} else {
$i=$i-1;
}
}
I'm basically trying to avoid 0, 1, and the letters O and I
What about not messing with magic numbers (besides position) and using PHP's range()?
$numbers = range(2, 9);
$letters = range('a', 'z');
unset($letters[8], $letters[14]);
$letters = array_merge($letters, array_map('strtoupper', $letters));
$pool = array_merge($numbers, $letters);
shuffle($pool);
$captcha = join(array_slice($pool, 0, $numITER)); // e.g. 2ESQcnMTNy
CodePad.
I'm tinkering with a domain name finder and want to favour those words which are easy to pronounce.
Example: nameoic.com (bad) versus namelet.com (good).
Was thinking something to do with soundex may be appropriate but it doesn't look like I can use them to produce some sort of comparative score.
PHP code for the win.
Here is a function which should work with the most common of words... It should give you a nice result between 1 (perfect pronounceability according to the rules) to 0.
The following function far from perfect (it doesn't quite like words like Tsunami [0.857]). But it should be fairly easy to tweak for your needs.
<?php
// Score: 1
echo pronounceability('namelet') . "\n";
// Score: 0.71428571428571
echo pronounceability('nameoic') . "\n";
function pronounceability($word) {
static $vowels = array
(
'a',
'e',
'i',
'o',
'u',
'y'
);
static $composites = array
(
'mm',
'll',
'th',
'ing'
);
if (!is_string($word)) return false;
// Remove non letters and put in lowercase
$word = preg_replace('/[^a-z]/i', '', $word);
$word = strtolower($word);
// Special case
if ($word == 'a') return 1;
$len = strlen($word);
// Let's not parse an empty string
if ($len == 0) return 0;
$score = 0;
$pos = 0;
while ($pos < $len) {
// Check if is allowed composites
foreach ($composites as $comp) {
$complen = strlen($comp);
if (($pos + $complen) < $len) {
$check = substr($word, $pos, $complen);
if ($check == $comp) {
$score += $complen;
$pos += $complen;
continue 2;
}
}
}
// Is it a vowel? If so, check if previous wasn't a vowel too.
if (in_array($word[$pos], $vowels)) {
if (($pos - 1) >= 0 && !in_array($word[$pos - 1], $vowels)) {
$score += 1;
$pos += 1;
continue;
}
} else { // Not a vowel, check if next one is, or if is end of word
if (($pos + 1) < $len && in_array($word[$pos + 1], $vowels)) {
$score += 2;
$pos += 2;
continue;
} elseif (($pos + 1) == $len) {
$score += 1;
break;
}
}
$pos += 1;
}
return $score / $len;
}
I think the problem could be boiled down to parsing the word into a candidate set of phonemes, then using a predetermined list of phoneme pairs to determine how pronouncible the word is.
For example: "skill" phonetically is "/s/k/i/l/". "/s/k/", "/k/i/", "/i/l/" should all have high scores of pronouncibility, so the word should score highly.
"skpit" phonetically is "/s/k/p/i/t/". "/k/p/" should have a low pronouncibility score, so the word should score low.
Use a Markov model (on letters, not words, of course). The probability of a word is a pretty good proxy for ease of pronunciation. You'll have to normalize for length, since longer words are inherently less probable.