I'm trying to write a method for a class Queue that inverts the whole Queue. After running the program, it gives the follow problem:
Cannot use object of type Queue as array on line echo($i.".
".$this->kolejka[$i-1]."<br>");
Apparently when he is trying to use printOut method again on the inverted Queue. Please help!
Please don't laugh (too hard) as I tried many things to make this work and I'm lost.
Here is the whole code:
<?php
class Queue
{
private $Queue = array(); //Init
public function clear() //Clears the Queue
{
$this->Queue = array();
}
public function isMember($item) //Returns True if element is in the Queue
{
foreach($this->Queue as $x)
{
if($item === $x)
{
return true;
}
}
return false;
}
public function remove() //Removes first element
{
return array_shift($this->Queue);
}
public function add($item) //Adds element to the end
{
$this->Queue[] = $item;
}
public function first() //Returns the first element
{
return current($this->Queue);
}
public function printOut() //Writes down in order all the elements
{
for($i=1;$i < count($this->Queue)+1;$i++)
{
echo($i.". ".$this->Queue[$i-1]."<br>");
}
}
public function length() //Returnts length
{
return count($this->Queue);
}
public function invert() //Reverts the Queue
{
$newQueue = new Queue();
for ($i = $this->length() - 1;$i>=0;$i--)
{
$newQueue->add($this->first());
$this->remove();
}
$this->Queue = $newQueue;
}
}
$kolej = new Queue();
$kolej->add("Apple");
$kolej->add("Orange");
$kolej->add("Banana");
$kolej->add("Mandarin");
$kolej->add("Raspberry");
echo $kolej->first()."<br>";
$kolej->remove();
echo $kolej->first()."<br>";
echo $kolej->isMember("Apple")."<br>";
echo $kolej->isMember("Orange")."<br>";
$kolej->printOut();
echo "Currently Queue is of length ".$kolej->length()."<br>";
$kolej->invert();
$kolej->printOut();
?>
your invert() function is doing the wrong thing. $this->Queue is supposed to be an array:
private $Queue = array(); //Init
, but at the end of the function you are setting it to an object (named $newQueue):
public function invert() //Reverts the Queue
{
$newQueue = new Queue();
for ($i = $this->length() - 1;$i>=0;$i--)
{
$newQueue->add($this->first());
$this->remove();
}
$this->Queue = $newQueue;
}
You can solve this in one of two ways:
set $this->Queue to $newQueue->Queue (you probably have to make it a non-private variable)
learn how to invert an array in place instead of creating a temporary array
Here is solid solution for the invert method. I hope this helps.
//Reverts the Queue
public function invert() {
$newQueue = array();
for ($i = 0; $i < count($this->Queue) + 1; $i++) {
$newQueue[$i] = $this->Queue[count($this->Queue) - 1 - $i];
echo $newQueue[$i] . '<br/>';
}
}
Related
In my programme I've a large array of strings(say 1600) which I want to show as a CheckBox list. The array is actually the location of all the songs in one's PC, and thus can gradually be bigger. I don't wanna use ListView<String> as the CheckBox list is more efficient and above all visually better for my purpose. I'm currently doing the below :
private void listAll() {
songs = MediaManager.getAllSongs();
VBox vb = new VBox();
vb.setSpacing(5);
vb.getStyleClass().add("background");
if (songs != null) {
Service s = new Service() {
#Override
protected Task createTask() {
Task t = new Task() {
#Override
protected Object call() throws Exception {
for (String song : songs) {
addSong(song, vb);
c++;
updateMessage(c+" songs");
}
return null;
}
};
t.messageProperty().addListener((obs,o,n)->{
count.setText(n);
});
return t;
}
};
s.start();
ScrollPane sp = new ScrollPane(vb);
getChildren().add(sp);
}
}
private void addSong(String n, Pane p) {
String toAdd = "";
Media m = new Media(Paths.get(n).toUri().toString());
if (m.getMetadata().get("title") == null || !(m.getMetadata().get("title").equals(""))) {
toAdd = m.getSource().split("/")[m.getSource().split("/").length - 1].replace("%20", " ").replace(".mp3", "");
} else {
toAdd = ((String) m.getMetadata().get("title"));
}
SongBox s = new SongBox(toAdd);
s.setUserData(n);
p.getChildren().add(s);
}
class SongBox extends CheckBox {
public SongBox(String t) {
this();
setText(t);
}
public SongBox() {
super();
setOnAction((ActionEvent evt) -> {
if (isSelected()) {
if (!playNow.isVisible()) {
playNow.setVisible(true);
}
path = (String) getUserData();
selected.add((String) getUserData());
} else {
selected.remove((String) getUserData());
if (selected.size() == 0) {
playNow.setVisible(false);
}
}
});
}
}
First of all, that is not showing the complete array. Whenever I'm going back and returning to it, the number of songs get changed. Secondly, the whole UI is getting sluggish(sometimes also hanging my PC). Moreover, I can't cancel the Service when I've gone to the previous window, as it's always returning false. Anyone have a better approach?
I would like to name the methods with an array like this
class MyClass {
private $_array = array();
public function __construct($array) {
$this->_array = $array; //this works!
}
//now, what i'm trying to do is:
foreach ($this->_array AS $methodName) {
public function $methodName.() {
//do something
}
}
}
What's the right way to do this?
when you working with classes and want something like dynamic methods - i think the magic method __call is the best way.
you can make it very easily:
class MyClass {
private $_array = array();
public function __construct($array) {
$this->_array = $array; //this works!
}
public function __call($method, $args) {
if(in_array($method, $this->_array)){
print "Method $method called\n";
//or you can make like this: return call_user_func_array($method, $args);
}
}
}
$obj = new MyClass(array("one","two"));
$obj->two(); // OUTPUT: Method two called
Im making an inventory and Im adding stacks but ive hit an issue
below is what I want compared to what works
I just want to find the index of the object I pass through
myArray[0] = [item:object,StackAmmount:int]
var myArray:Array = new Array();
myArray[0] = ["name1",1];
myArray[1] = ["name2",1];
myArray[2] = ["name3",1];
trace("Name" , myArray[0][0]);
//traces "name1"
trace("Stack" , myArray[0][1]);
//traces "1"
trace("Index of Object" , myArray.indexOf("name2"));
//traces -1
// Not Working (NOT FOUND)
//How can I find the index of "item1" or "item2" in the above example
var myOtherArray:Array = new Array();
myOtherArray[0] = "name1";
myOtherArray[1] = "name2";
myOtherArray[2] = "name3";
trace("Name" , myOtherArray[0]);
//traces "name1"
trace("Index of Object" , myOtherArray.indexOf("name2"));
//traces 1
//Working
perhaps there is a better way of dealing with stacks?
Paste Bin Link: http://pastebin.com/CQZWFmST
I would use a custom class, therefore a 1D vector would be enough. The class would contain the name of the item, and the stack. You could subclass this class to override the maxStack variable of the item, and then the searches would be easier aswell, you could just iterate through the vector and check the name.
public class InventoryItem
{
protected var _name:String;
protected var _stack:int;
protected var _maxStack:int;
public function InventoryItem():void {
}
public function get name():String {
return _name;
}
public function get stack():int {
return _stack;
}
public function set stack(value:int):void {
_stack = value;
}
public function get maxStack():int {
return _maxStack;
}
}
...
public class InventoryWeapon extends InventoryItem
{
public function InventoryWeapon(__name:String, startStack:int) {
_maxStack = 64;
_name = __name;
_stack = startStack;
}
}
I have encountered quite a challenging one and I'd love to get some support.
Here is the scenario :
The main Game class instances the Level1 Class in charge for spawning enemies through nested For loops and push them to an array.
It then checks for collisions between the Bullet and Enemy and if it find a collision it calls a method in the Enemy class that removes removeChild and Splice itself from the array.
The thing is it works for the first few enemies, and then it will pick the wrong Enemy to destroy, and stop completely to function.
I tried using indexOf to be sure I am referring to the right object, but to no avail.
I think the Pslice and removeChild are pointing to different objects.
This mess happended when I moved the removeChild and splice from the Game Class to the Enmy class
Link to the work in progress : https://dl.dropboxusercontent.com/s/69hcmzygnkx7h1e/space_shooter.swf
I'd like some help on this one.
Thank you !!!
Main class : Game.AS
package
{
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.events.Event;
import flash.text.*;
import flash.geom.Point;
public class Game extends MovieClip
{
public var _instance : Game;
public var player:Player;
public static var level1:Level1;
public var bullet:Bullet;
private var bullets_arr:Array;
var fire_on : Boolean;
var fire_counter : int;
public function Game()
{
level1=new Level1(this.stage);
player = new Player ;
addChild(player);
player.y = 600;
bullets_arr = [];
addEventListener(Event.ENTER_FRAME,Main);
stage.addEventListener(MouseEvent.MOUSE_DOWN,mouseDownHandler);
stage.addEventListener(MouseEvent.MOUSE_UP,mouseUpHandler);
}
function mouseDownHandler($e:MouseEvent):void
{
fire_on = true;
}
function mouseUpHandler($e:MouseEvent):void
{
fire_on = false;
fire_counter = 0;
}
function fire():void
{
bullet = new Bullet ;
addChild(bullet);
bullet.x = player.x;
bullet.y = player.y - 32;
bullets_arr.push(bullet);
}
public function Main(e: Event):void
{
player.x = mouseX;
if (bullets_arr)
{
for (var m:int = 0; m < bullets_arr.length; m++)
{
bullets_arr[m].y -= 20;
if(Game.level1.enemies_arr)
{
for (var n:int = 0; n < Game.level1.enemies_arr.length; n++)
{
if (Game.level1.enemies_arr[n].hitTestObject(bullets_arr[m]))
{
if(bullets_arr[m].parent)
{
bullets_arr[m].parent.removeChild(bullets_arr[m]);
bullets_arr.splice(bullets_arr[m],1);
Game.level1.enemies_arr[n].Damage(10, Game.level1.enemies_arr[n]);
}
}
}
}
}
}
if(fire_on)
{
fire_counter++;
if(fire_counter == 01)
{
fire();
}
else if(fire_counter >2)
{
fire_counter =0;
}
}
}
}
}
Level1.as where the enemies are spawned and pushed to the array.
package
{
import flash.display.MovieClip;
import flash.display.Stage;
import flash.events.Event;
public class Level1 extends MovieClip
{
var i:int;
var j:int;
var frame :int;
public var enemy:Enemy;
public var enemies_arr:Array;
public function Level1(target:Stage)
{
frame = 0;
enemies_arr = [];
for (var i:int = 0; i < 5; i++)
{
for (var j:int = 0; j < 3; j++)
{
enemy = new Enemy;
enemy.x = j*100 + 260;
enemy.y = i*40 - 150;
target.addChild(enemy);
enemies_arr.push(enemy);
trace(enemy.parent);
}
}
}
}
}
The Enemy class Enemy.AS
package
{
import flash.display.MovieClip;
public class Enemy extends MovieClip
{
var Health : int;
function Enemy()
{
Health =2;
}
public function Damage(Damage:int, enemyHit:Enemy)
{
Health -= Damage;
if (Health <1)
{
Die(enemyHit);
}
}
private function Die(enemyHit:Enemy)
{
if(enemyHit.parent)
{
this.parent.removeChild(this);
Game.level1.enemies_arr.splice(Game.level1.enemies_arr.indexOf(enemyHit,1));
}
}
}
}
You should traverse both Game.level1.enemies_arr and bullets_arr backwards. The point is, splice() shortens the array, shifts the elements that are in greater positions than the spliced one(s) to lesser indexes, and the loop counter is not automatically adjusted. The error is pretty common, but is often overlooked. Also, with bullets_arr you can get out of the array causing a 1009 error if your last bullet out of bullets_arr will hit an enemy.
A small nitpick: You are checking for the array's existence within a loop, and for another array's existence once within the enter frame listener. In fact you should either initialize them with at least new Array() or [] before you add an event listener to the Main object, or wherever this is assigned to, or check like if (!bullets_arr) bullets_arr=new Array(); and leave it at that, so one check of the array's existence will be needed.
public function Main(e: Event):void
{
player.x = mouseX;
if (!bullets_arr) bullets_arr=new Array();
if (!Game.level1.enemies_arr) throw new Error('Hey programmer, initialize your arrays!');
// ^ bad practice to throw exceptions in listeners, but if you get one, you've coded something wrongly.
for (var m:int=bullets_arr.length-1;m>=0;m--) {
var bm:Bullet=bullets_arr[m]; // TODO fix type
// the local variable is a cleaner and faster approach
bm.y-=20;
for (var n:int=Game.level1.enemies_arr.length-1;n>=0;n--) {
if (!bm) break; // bullet was destroyed, why checking more enemies vs that?
if (Game.level1.enemies_arr[n].hitTestObject(bm)) {
bm.parent.removeChild(bm);
bullets_arr.splice(m,1); // splice is done by index, not by object
bm=null; // oops, missed this. The bullet hit something and is now lost
// so we null the local var, so we can break out the loop.
Game.level1.enemies_arr[n].Damage(10, Game.level1.enemies_arr[n]);
}
}
}
// rest of your code follows here
}
I finally found the problem, it was very stupid of me :
I just mistyped the Die function in the Enemy class :
I have written :splice(Game.level1.enemies_arr.indexOf(enemyHit,1)) instead of splice(Game.level1.enemies_arr.indexOf(enemyHit),1)
Anyway I learnt a lot of think by trying to fix this error.
Thanks
In .Net it is possible to iterate through an enumeration by using
System.Enum.GetNames(typeof(MyEnum))
or
System.Enum.GetValues(typeof(MyEnum))
In Silverlight 3 however, Enum.GetNames and Enum.GetValues are not defined. Does anyone know an alternative?
Or maybe strongly typed using linq, like this:
public static T[] GetEnumValues<T>()
{
var type = typeof(T);
if (!type.IsEnum)
throw new ArgumentException("Type '" + type.Name + "' is not an enum");
return (
from field in type.GetFields(BindingFlags.Public | BindingFlags.Static)
where field.IsLiteral
select (T)field.GetValue(null)
).ToArray();
}
public static string[] GetEnumStrings<T>()
{
var type = typeof(T);
if (!type.IsEnum)
throw new ArgumentException("Type '" + type.Name + "' is not an enum");
return (
from field in type.GetFields(BindingFlags.Public | BindingFlags.Static)
where field.IsLiteral
select field.Name
).ToArray();
}
I figured out how to do this without making assumptions about the enum, mimicking the functions in .Net:
public static string[] GetNames(this Enum e) {
List<string> enumNames = new List<string>();
foreach (FieldInfo fi in e.GetType().GetFields(BindingFlags.Static | BindingFlags.Public)){
enumNames.Add(fi.Name);
}
return enumNames.ToArray<string>();
}
public static Array GetValues(this Enum e) {
List<int> enumValues = new List<int>();
foreach (FieldInfo fi in e.GetType().GetFields(BindingFlags.Static | BindingFlags.Public)) {
enumValues.Add((int)Enum.Parse(e.GetType(), fi.Name, false));
}
return enumValues.ToArray();
}
I haven't tried this, but the reflection APIs should work.
I belive this is the same as in the .NET Compact Framework. If we make the assumption that your enum values start at 0 and use every value until their range is over the following code should work.
public static IList<int> GetEnumValues(Type oEnumType)
{
int iLoop = 0;
bool bDefined = true;
List<int> oList = new List<int>();
//Loop values
do
{
//Check if the value is defined
if (Enum.IsDefined(oEnumType, iLoop))
{
//Add item to the value list and increment
oList.Add(iLoop);
++iLoop;
}
else
{
//Set undefined
bDefined = false;
}
} while (bDefined);
//Return the list
return oList;
}
Obviously you could tweak the code to return the enum names or to match diferent patterns e.g. bitwise values.
Here is an alternate version of the method that returns a IList<EnumType>.
public static IList<T> GetEnumValues<T>()
{
Type oEnumType;
int iLoop = 0;
bool bDefined = true;
List<T> oList = new List<T>();
//Get the enum type
oEnumType = typeof(T);
//Check that we have an enum
if (oEnumType.IsEnum)
{
//Loop values
do
{
//Check if the value is defined
if (Enum.IsDefined(oEnumType, iLoop))
{
//Add item to the value list and increment
oList.Add((T) (object) iLoop);
++iLoop;
}
else
{
//Set undefined
bDefined = false;
}
} while (bDefined);
}
//Return the list
return oList;
}