Basically, I'm trying to create a frame around text and on the third line where the text goes, it doesn't print out the # that's supposed to be the "padding" around it.
******************
*################*
*#HELLO, JUSTIN!*
*################*
******************
That's what it looks like - after the ! in the $greeting variable, there should be a #. The code is below, could anyone explain why this happens?
<html>
<body><?php
$pad = 1;
$rows = ($pad * 2) + 3;
$greeting = "HELLO, JUSTIN!";
$col = strlen($greeting) + (2 * $pad) + 2;
for ($r = 0; $r != $rows; ++$r)
{
for ($c = 0; $c != $col; ++$c)
{
if ($r == $pad + 1 && $c == $pad + 1)
{
echo $greeting;
$c += strlen($greeting);
//echo "#";
}
else
{
if ($r == 0 || $r == $rows - 1 || $c == 0 || $c == $col - 1)
echo "*";
else
echo "#";
}
}
echo "<br />";
}
?></body>
</html>
The vertical padding (above and below) the $greeting is easy to do, the trickiest part seems to be getting the row containing $greeting to display right.
Try using <= (less-than-or-equal-to) in your for-loop, instead of != (not-equal[s]).
You will have to change the ranges on your if statements.
$text = "justin rocks my socks!";
$asteriskAt = 0;
$poundAt = 1;
$padding_left = 2;
$padding_right = 2;
//table looks like
//ABCDEFG
//HIJKLMN
//OPQRSTU
//row view
//0000000
//1111111
//3333333
//column view
//0123456
//0123456
//0123456
// ^ all the same "table" (anything with x and y, or rows and cols), just different ways of viewing it.
$num_cols = strlen($text) + ($padding_left + $padding_right); //so far so good?
//strlen($text) evaluates to 22
//$num_cols evaluates to 26 ( = 22 + 2 + 2)
for ($c = 0; $c <= $num_cols; ++$c) {
if ($c == $asteriskAt || $c == $num_cols - $asteriskAt) {
echo '*';
}
if ($c == $poundAt || $c == $num_cols - $poundAt) {
echo '#';
}
if ($c > $poundAt || $c < $num_cols - $poundAt) {
// is it clear why I picked this "range" ?
// if you do $c > 2 || $c < $num_cols - 2
// you will display something different
// because the cols and rows are zero-indexed
//
//cols
// 0123456
// ^2 is actually slot #3
//our thing will look like
//01___56
//compared with
//0123456
//01___56 <- (padding dudes)
//0123456
$idx = $c - $padding_left;
echo $text[$idx];
}
}
By the way, www.ideone.com has a PHP interpreter, too! awesome.
Check out the differences in these 2 outputs, and notice that I only changed the one operator in the for-loop:
http://ideone.com/M1lpM //asterisk :D
http://ideone.com/JqsTx //no last asterisk =(
the code you posted is not bad, especially if it works on the first try!
but really, programming and software in general demands precision, and you can't get angry at a computer for doing what you tell it. Thus, it's much better to work in stages or steps, for when you could use clarity in debugging =)
Programmers learn gradually (if they are stubborn like me) that it's not only about having the code work 2 minutes from when you started writing it, it's about having it be readable and change-able, maintainable in the future. It's a good habit to develop, since you never know how long it will be before you look at your code again "what the heck was this crazy person doing?!" you might think, then realize you wrote it a year ago ...and you never know who will end up maintaining whatever you make, whether you contribute to an open source project or work on a commercial application.
So, some suggestions (that I would tell my 6-years-ago-self, really) :
if (X || Y || Z || Q || P || NINE || WOLFHOUNDS || CATS || LION) {
//this will be incredibly hard to debug, so try to avoid it
//because it's basically a gateway, a CHANNEL into pandora's box
//and pandora's heart and soul
//and it's not fun in pandora's part of the universe
//although she is just trying to be happy like everyone else
//and we should not really discredit her opinions
//but man is this if statement going to be bothersome
//if one little thing goes wrong
//and we gotta figure out what caused it
}
If you have any questions about the code I posted, don't hesitate to ask =)
hopefully this helps you figure out why the code you posted doesn't work the way you expected it to, but you can very easily make a Gordian Knot that is very difficult to cut through without perfect wisdom / a life of meditation and moderation
$c += strlen($greeting);
must be replaced with
$c += strlen($greeting)-1;
You forgot to accommodate for the padding:
Change
$c += strlen($greeting);
to
$c += strlen($greeting) - $pad;
Related
So Minecraft uses section signs (§) for colour coding so for example, light green is §a (a is the color code id for green). An important note to remember is that these are VISUALLY ignored in-game. I'm using wordwrap() to make text look centred however these section signs get in the way of that because they're visually not there yet still considered as characters by the function itself.
Here's my attempt: if you take a look, I tried to count the number of occurrences the section sign was found and multiplied it by two for the colour code character. I later then realized that this is inefficient because this affects the entire line of code and not just a specific bit. This basically means that this would make the length of other colour coded lines look odd since they have more or less colour coding in them. I also tried a rather dumb alternative where I'd use constants but I quickly realized that wasn't going to work. Let me know if anything is unclear. Thanks in advance.
$line = "§r§7This is the §eAuction House§7! In the §eAuction House§7, you can sell and purchase items from other Luriders who have auctioned their items. The §eAuction House §7is a great way to make some cash by simply selling items that other players might be interested in buying."
public static function itemLineOptimizer(string $line, int $width = 40)
{
$width += substr_count($line, '§') * 2;
return wordwrap($line, $width, "\n");
}
Console Output:
string(281) "§r§7This is the §eAuction House§7! In the §eAuction
House§7, you can sell and purchase items from other
Luriders who have auctioned their items. The §eAuction
House §7is a great way to make some cash by simply
selling items that other players might be interested in
buying."
In-Game Output:
In-Game Output
No where near as efficient as IMSoP's approach, but it is an alternative method I wanted to share. So what I did was I replaced section signs, removed them, wordwrapped, then added them back to their correct places. A bit complicated at first look but it's quite simple. Every line has its details commented.
function itemLineOptimizer(string $line, int $width = 40)
{
$line = str_replace("§", "&", $line); // Since section signs aren't just one-byte, we're going to make our lives easier and replace them with another one-byte symbol, I went with "&"
$colourCoding = array(); // Straightforward
$split = str_split($line); // Splitting the line into an array per character
foreach ($split as $key => $char){ // for every character has a $key (position) and the character itself: $char
if($char === "&") { // Check if it's a section sign / symbol chosen
array_push($colourCoding, [$key, $split[$key + 1]]); // add to $colourCoding an element which includes an array consisting of the position of the sign and the colour which the character at the position after
unset($split[$key]); // remove sign
unset($split[$key + 1]); // remove colour
}
}
// Now we've removed all colour coding from the line and saved it in $colourCoding
$bland = wordwrap(implode("", $split), $width, "\n"); // $bland is the now colourless wordwrapped line
foreach ($colourCoding as $array){ // Lastly we add the section signs back in their positions
$key = $array[0]; // position
$colour = $array[1]; // colour
$lineBreak = substr_count($bland, "§"); // Check for section signs already inside this line: they interfere with future loops since the correct position is different
$bland = substr_replace($bland, "§".$colour, $key + $lineBreak, 0); // Adding the colour coding back back to its correct position
}
return $bland; // Straightforward
}
$line = "§r§7This is the §eAuction House§7! In the §eAuction House§7, you can sell and purchase items from other Luriders who have auctioned their items. The §eAuction House §7is a great way to make some cash by simply selling items that other players might be interested in buying.";
var_dump(wordwrap($line, 40), itemLineOptimizer($line, 40));
One way to approach this which I though might be interesting is to take the internal implementation of wordwrap, and adapt it to our needs.
So I found the definition in the source, and in particular the special-case algorithm for handling a single-character line-break character which is all we need here, and saves us understanding all the other modes.
It works by copying the string, and then walking through it character by character, tracking when it last saw a space, and when it last saw or inserted a newline character. It then over-writes spaces with newline characters in place, without having to touch the rest of the string.
I first translated that literally into PHP (mostly a case of adding $ in front of each variable, and removing some special type handling macros), giving this:
function my_word_wrap($text, $linelength)
{
$newtext = $text;
$breakchar = "\n";
$laststart = $lastspace = 0;
$string_length = strlen($text);
for ($current = 0; $current < $string_length; $current++) {
if ( $text[$current] == $breakchar ) {
$laststart = $lastspace = $current + 1;
}
elseif ( $text[$current] == ' ' ) {
if ($current - $laststart >= $linelength) {
$newtext[$current] = $breakchar;
$laststart = $current + 1;
}
$lastspace = $current;
}
elseif ($current - $laststart >= $linelength && $laststart != $lastspace) {
$newtext[$lastspace] = $breakchar;
$laststart = $lastspace + 1;
}
}
return $newtext;
}
Two of those if statements include this condition which tracks how many characters we've seen since the last line break: $current - $laststart >= $linelength. What we could do is subtract from that the number of invisible characters we've seen, so they don't contribute to the "width" of lines: $current - $laststart - $invisibles >= $linelength.
Next, we need to detect section signs. My immediate guess was to use $text[$current] == '§', but that doesn't work because we're working in byte offsets, and § is not a single byte. Assuming UTF-8, it's specifically the pair of bytes which in hexadecimal are C2 A7, so we need to test the current and next character for that pair: $text[$current] == "\xC2" && $text[$current+1] == "\xA7".
Now we can detect the invisible characters, we can increment our $invisibles counter. Since § is two bytes, and the following character is also invisible, we want to increment the counter by three, and also move the $current pointer an extra two steps:
elseif ( $text[$current] == "\xC2" && $text[$current+1] == "\xA7" ) {
$invisibles += 3;
$current += 2;
}
Finally, we need to reset the $invisibles counter whenever we insert a newline, or see an existing one - in other words, everywhere we reset $laststart.
So, the final result looks like this:
function special_word_wrap($text, $linelength)
{
$newtext = $text;
$breakchar = "\n";
$laststart = $lastspace = $invisibles = 0;
$string_length = strlen($text);
for ($current = 0; $current < $string_length; $current++) {
if ( $text[$current] == $breakchar ) {
$laststart = $lastspace = $current + 1;
$invisibles = 0;
}
elseif ( $text[$current] == ' ' ) {
if ($current - $laststart - $invisibles >= $linelength) {
$newtext[$current] = $breakchar;
$laststart = $current + 1;
$invisibles = 0;
}
$lastspace = $current;
}
elseif ( $text[$current] == "\xC2" && $text[$current+1] == "\xA7" ) {
$invisibles += 3;
$current += 2;
}
elseif ($current - $laststart - $invisibles >= $linelength && $laststart != $lastspace) {
$newtext[$lastspace] = $breakchar;
$laststart = $lastspace + 1;
$invisibles = 0;
}
}
return $newtext;
}
Here's a live demo of it in action with your sample input.
Not the most elegant, and probably not the most efficient way to do it, but I enjoyed the exercise, even if it's not what you were hoping for. :)
I am currently teaching myself web development/ programming and to learn php i have built a simple program. The program takes user input and based on a series of math algorithms and calculates 7 random lottery numbers. The code is working fine but i want to improve it. The code is very repetitive and i want to simplify it by creating my own functions. I have created the first function that takes the users input, simply does some maths and then returns some values.
For Example...
<?php
function some_maths($int1 $int2 $int3){
$x = $int1 + $int2;
$y = $int2 * $int3;
$z = $y * $x;
return $x
....}
So this is pretty straight forward, but what i want to do now is take the values of X, Y, Z and create a function that checks to make sure they're not matching, or that they're not less than 1 or greater than 59. I used a while loop in my original code that goes like this:
while($x == $y || $x == $z || $x <1 || $x >59){
if( x> 59 || x < 1){
if (x<1){
do{ $x+=$int}while($x <1);
}elseif ($x > 59){
do{ $x-=$int}while($x >59);
}else $x++;
}
This seems to work fine but i don't want to have to repeat the same code over and over. I am sure there has to be a better way? Could i put the values into an array and maybe do it that way? What would be the best solution for this?
Your question is kind of vague but if I had to write a function to check if three numbers weren't equal and were < 59 and >1 this is how I would do it
function validateNumbers($x , $y , $z)
{
if(equal($x,$y)) return false;
if(equal($x,$z)) return false;
if(equal($y,$z)) return false;
if($x>59||$x<1) return false;
if($y>59||$y<1) return false;
if($z>59||$z<1) return false;
return true;
}
function equal($x , $y)
{
if($x == $y)return true;
else return fasle;
}
So far I only see two (pretty straightforward) things:
Your function prototype in the first example is missing commas between the parameters. Instead of function some_maths($int1 $int2 $int3) it should read function some_maths($int1, $int2, $int3).
In your second example a closing } is missing. But if I am interpreting your stuff correctly, the outer if-clause is redundant. Thus, the snippet can be simplified to:
Second example:
while($x == $y || $x == $z || $x <1 || $x >59){
if (x<1){
do{ $x+=$int}while($x <1);
}
elseif ($x > 59){
do{ $x-=$int}while($x >59);
}
else $x++;
}
There may be more room for improvement (e.g. slim down the condition of the outer while loop) - but for that we would need more context (what happens before your loop, what is $int, ...).
I need to find the value of x where the variance of two results (which take x into account) is the closest to 0. The problem is, the only way to do this is to cycle through all possible values of x. The equation uses currency, so I have to check in increments of 1 cent.
This might make it easier:
$previous_var = null;
$high_amount = 50;
for ($i = 0.01; $i <= $high_amount; $i += 0.01) {
$val1 = find_out_1($i);
$val2 = find_out_2();
$var = variance($val1, $val2);
if ($previous_var == null) {
$previous_var = $var;
}
// If this variance is larger, it means the previous one was the closest to
// 0 as the variance has now started increasing
if ($var > $previous_var) {
$l_s -= 0.01;
break;
}
}
$optimal_monetary_value = $i;
I feel like there is a mathematical formula that would make the "cycling through every cent" more optimal? It works fine for small values, but if you start using 1000's as the $high_amount it takes quite a few seconds to calculate.
Based on the comment in your code, it sounds like you want something similar to bisection search, but a little bit different:
function calculate_variance($i) {
$val1 = find_out_1($i);
$val2 = find_out_2();
return variance($val1, $val2);
}
function search($lo, $loVar, $hi, $hiVar) {
// find the midpoint between the hi and lo values
$mid = round($lo + ($hi - $lo) / 2, 2);
if ($mid == $hi || $mid == $lo) {
// we have converged, so pick the better value and be done
return ($hiVar > $loVar) ? $lo : $hi;
}
$midVar = calculate_variance($mid);
if ($midVar >= $loVar) {
// the optimal point must be in the lower interval
return search($lo, $loVar, $mid, $midVar);
} elseif ($midVar >= $hiVar) {
// the optimal point must be in the higher interval
return search($mid, $midVar, $hi, $hiVar);
} else {
// we don't know where the optimal point is for sure, so check
// the lower interval first
$loBest = search($lo, $loVar, $mid, $midVar);
if ($loBest == $mid) {
// we can't be sure this is the best answer, so check the hi
// interval to be sure
return search($mid, $midVar, $hi, $hiVar);
} else {
// we know this is the best answer
return $loBest;
}
}
}
$optimal_monetary_value = search(0.01, calculate_variance(0.01), 50.0, calculate_variance(50.0));
This assumes that the variance is monotonically increasing when moving away from the optimal point. In other words, if the optimal value is O, then for all X < Y < O, calculate_variance(X) >= calculate_variance(Y) >= calculate_variance(O) (and the same with all > and < flipped). The comment in your code and the way have you have it written make it seem like this is true. If this isn't true, then you can't really do much better than what you have.
Be aware that this is not as good as bisection search. There are some pathological inputs that will make it take linear time instead of logarithmic time (e.g., if the variance is the same for all values). If you can improve the requirement that calculate_variance(X) >= calculate_variance(Y) >= calculate_variance(O) to be calculate_variance(X) > calculate_variance(Y) > calculate_variance(O), you can improve this to be logarithmic in all cases by checking to see how the variance for $mid compares the the variance for $mid + 0.01 and using that to decide which interval to check.
Also, you may want to be careful about doing math with currency. You probably either want to use integers (i.e., do all math in cents instead of dollars) or use exact precision numbers.
If you known nothing at all about the behavior of the objective function, there is no other way than trying all possible values.
On the opposite if you have a guarantee that the minimum is unique, the Golden section method will converge very quickly. This is a variant of the Fibonacci search, which is known to be optimal (require the minimum number of function evaluations).
Your function may have different properties which call for other algorithms.
Why not implementing binary search ?
<?php
$high_amount = 50;
// computed val2 is placed outside the loop
// no need te recalculate it each time
$val2 = find_out_2();
$previous_var = variance(find_out_1(0.01), $val2);
$start = 0;
$end = $high_amount * 100;
$closest_variance = NULL;
while ($start <= $end) {
$section = intval(($start + $end)/2);
$cursor = $section / 100;
$val1 = find_out_1($cursor);
$variance = variance($val1, $val2);
if ($variance <= $previous_var) {
$start = $section;
}
else {
$closest_variance = $cursor;
$end = $section;
}
}
if (!is_null($closest_variance)) {
$closest_variance -= 0.01;
}
everybody!
I'm new on Python and I'm using version 3.
I already program in PHP and I have done a script where I enter a string (a name like 'John') and the script returns the number associated to this name, based on a calculation from ASCii table.
The formula is => (ASCii code - 65)%9+1.
Here's my script in PHP:
<?php
$entry=strtoupper("Jack");
$value = 0;
for ($i = 0; $i < strlen($entry); $i++) {
if ($entry[$i] >= "A" && $entry[$i] <= "Z") {
$temp = (ord($entry[$i]) - 65)%9 + 1;
$value += $temp;
}
}
$result = $value%9;
if ($result == 0) $result = 9;
echo $result;
?>
The above result should be 7.
Here's my script in Python:
entry = input("Type your name: ")
name = entry.upper()
value = 0
for letter in range(len(name)):
while letter:
temp = int(ord(name[letter])-65)%9+1
value += temp
result = value%9
if result == 0:
result = 9
print(result)
Well, it's not working, because it seems that Python doesn't run through letters the same way I did in PHP on the first IF statement I used.
Does anybody know how can I solve this?
Thanks in advance!!!
God bless you all!
There are a few problems with your python code.
The biggest one is the if from php somehow becoming a while(and an infinite one) in python.
The direct translation to python would be(and it should make the code work):
if 'A' <= name[letter] <= 'Z':
But, you should never loop through indices in python.
Instead, loop directly over the values you want to work with.
Instead of:
for i in range(len(some_list)):
# do stuff with some_list[i]
You should do:
for item in some_list:
# do stuf with item
Edit:
This is how the loop would look written in a more pythonic way:
for letter in name:
if 'A' <= letter <= 'Z':
value += (ord(letter) - 65) % 9 + 1
Also, you can see here that the int() call and the temp variable are not really needed.
Please consider the following code:
$start = microtime();
for($i = 2; $i < 100; $i++)
{
for($y = 2; $y <= sqrt($i); $y++)
{
if($i%$y != 0)
{
continue;
}
else
{
continue 2;
}
}
echo $i.',';
}
echo "\nFinished in " . (microtime() - $start);
Given that the above code effectively uses continue 2 to break the inner loop and skip any code post the inner loop, why does the following code on average execute faster when it appears to do more:
$start = microtime();
for($i = 2; $i < 100; $i++)
{
$flag = true;
for($y = 2; $y <= sqrt($i); $y++)
{
if($i%$y != 0)
{
continue;
}
else
{
$flag = false;
break;
}
}
if($flag === true) echo $i.',';
}
echo "\nFinished in " . (microtime() - $start);
Thanks for any input.
_____ Update ____________
Thanks for the feedback but we seem to have missed the point. Regardless of if this is good programming practice I was trying to understand why the performance difference (which is tiny but consistent) is not within the bias I expected.
The passing of true to microtime seems insignificant as both samples are measured using the same method with the same overhead and the same inaccuracy.
More than one run was tested, as implied by use of the word average.
Just for illustration please consider the following small samples using microtime(true) which shows the same pattern as using microtime().
I know this is a small sample but the pattern is quite clear:
Continue
0.00037288665771484
0.00048208236694336
0.00046110153198242
0.00039386749267578
0.0003662109375
Break
0.00033903121948242
0.00035715103149414
0.00033307075500488
0.00034403800964355
0.00032901763916016
Thanks for looking, and thanks for any further feedback.
______ UPDATE Further investigation ____________
Interestingly if the echo statements are removed from the code the continue performs faster, with the echo statements in place break is faster.
Please consider the following code sample, and consider that the results are in conflict dependant on if the echo statements are removed or not:
<?php
$breakStats = array();
$continueStats = array();
ob_start();
for($i = 0; $i < 10000; $i++)
{
$breakStats[] = doBreakTest();
$continueStats[] = doContinueTest();
}
ob_clean();
echo "<br/>Continue Mean " . (array_sum($continueStats) / count($continueStats));
echo "<br/>Break Mean " . (array_sum($breakStats) / count($breakStats));
function doBreakTest()
{
$start = microtime(true);
for($i = 2; $i < 100; $i++)
{
$flag = true;
$root = sqrt($i);
for($y = 2; $y <= $root; $y++)
{
if($i%$y != 0)
{
continue;
}
else
{
$flag = false;
break;
}
}
}
if($flag === true) echo $i . '';
return microtime(true) - $start;
}
function doContinueTest()
{
$start = microtime(true);
for($i = 2; $i < 100; $i++)
{
$root = sqrt($i);
for($y = 2; $y <= $root; $y++)
{
if($i%$y != 0)
{
continue;
}
else
{
echo $i . '';
continue 2;
}
}
}
return microtime(true) - $start;
}
Echo statements present :
Continue Mean 0.00014134283065796
Break Mean 0.00012669243812561
Echo statements not present :
Continue Mean 0.00011746988296509
Break Mean 0.00013022310733795
Note that by removing the echo statement from the break and flag test we also remove the ($flag === true) check, so the load should reduce, but continue in this case still wins. W
So in a pure continue n versus break + flag scenario, it appears that continue n is the faster contstruct. But add an equal number of identical echo statements, and the continue n performance flags.
This makes sense to me logically that continue n should be faster, but I would have expected to see the same with the echo statements present.
This is clearly a difference in the generated opcodes, and the position of the echo statement (inner loop vs outer loop) does anyone know a way of seeing the opcodes generated? This I suppose is ultimatley what I need as I am trying to understand what is happening internally.
Thanks :)
Yes, first one is bit faster. It's because it just jumps out on continue 2 and prints $i.
2nd example has more job to do... assign value to $flag variable, jumps out of loop, checks $flag's value, checks $flag's type (compares too) and then prints out $i. It's bit slower (simple logic).
Anyways, has it any purpose?
Some of my results for comparing
0.0011570 < 0.0012173
0.0011540 < 0.0011754
0.0011820 < 0.0012036
0.0011570 < 0.0011693
0.0011970 < 0.0012790
Used: PHP 5.3.5 # Windows (1000 attempts; 100% first was faster)
0.0011570 < 0.0012173
0.0005000 > 0.0003333
0.0005110 > 0.0004159
0.0003900 < 0.0014029
0.0003950 > 0.0003119
0.0003120 > 0.0002370
Used: PHP 5.3.3 # Linux (1000 attempts; 32% first : 68% second was faster)
0.0006700 > 0.0004863
0.0003470 > 0.0002591
0.0005360 > 0.0004027
0.0004720 > 0.0004229
0.0005300 > 0.0004366
Used: PHP 5.2.13 # Linux (1000 attempts; 9% first : 91% second was faster)
Sorry, I don't have any more servers for testing :) Now I think it mostly depends of hardware (and maybe depends of OS too).
Generally: It proves only that Linux server is faster than one run at Windows :)
The continue 2 version is slightly faster for me. But these aren't the types of things you generally need to concern yourself with. Consider:
for($y = 2; $y <= sqrt($i); $y++)
Here you are calculating the sqrt on every iteration. Just changing that to:
$sqrt = sqrt($i);
for($y = 2; $y <= $sqrt; $y++)
will give you a much better improvement than switching between two nearly identical loop styles.
The continue 2 should be used if you find that it's easier for you to understand. The computer doesn't really care.
To address your update regarding looking at opcodes, see:
http://pecl.php.net/package/vld
php -d vld.active=1 -d vld.execute=0 -f foo.php